From 690130fe0b4c1a72c931e9aafbec8f21dbeed20c Mon Sep 17 00:00:00 2001 From: Alan Date: Mon, 20 Jan 2014 15:03:04 -0300 Subject: [media] dvb-frontends: Add static Add static to tda m_* variables in the header. They don't need to be global. With some cleanup they could probably even be marked const. Resolves-bug: https://bugzilla.kernel.org/show_bug.cgi?id=68191 Reported-by: Christian Schneider Signed-off-by: Alan Cox Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/r820t.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 96ccfebce7ca..a0db64f57a98 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1545,7 +1545,7 @@ static int r820t_imr_cross(struct r820t_priv *priv, cross[i].value = rc; if (cross[i].value < tmp.value) - memcpy(&tmp, &cross[i], sizeof(tmp)); + tmp = cross[i]; } if ((tmp.phase_y & 0x1f) == 1) { /* y-direction */ -- cgit v1.2.1 From a83d7d17ad4c920b9b3f5330521fa346dddd3de3 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 9 Jul 2014 18:37:30 -0300 Subject: [media] si2157: implement sleep Implement sleep for power-management. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 271a752cee54..3f88e5365850 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -89,12 +89,23 @@ static int si2157_init(struct dvb_frontend *fe) static int si2157_sleep(struct dvb_frontend *fe) { struct si2157 *s = fe->tuner_priv; + int ret; + struct si2157_cmd cmd; dev_dbg(&s->client->dev, "%s:\n", __func__); s->active = false; + memcpy(cmd.args, "\x13", 1); + cmd.len = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + return 0; +err: + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; } static int si2157_set_params(struct dvb_frontend *fe) -- cgit v1.2.1 From e6b4380f3ef89601b1a77c327fe3aa7b5500b3f4 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 10 Jul 2014 06:02:53 -0300 Subject: [media] si2157: add read data support for fw cmd func We want also read data from firmware. Add support for it. Copied from si2168 driver. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 74 +++++++++++++++++++++----------------- drivers/media/tuners/si2157_priv.h | 3 +- 2 files changed, 43 insertions(+), 34 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 3f88e5365850..a4908ee81ab9 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -20,50 +20,52 @@ static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) { int ret; - u8 buf[1]; unsigned long timeout; mutex_lock(&s->i2c_mutex); - if (cmd->len) { + if (cmd->wlen) { /* write cmd and args for firmware */ - ret = i2c_master_send(s->client, cmd->args, cmd->len); + ret = i2c_master_send(s->client, cmd->args, cmd->wlen); if (ret < 0) { goto err_mutex_unlock; - } else if (ret != cmd->len) { + } else if (ret != cmd->wlen) { ret = -EREMOTEIO; goto err_mutex_unlock; } } - /* wait cmd execution terminate */ - #define TIMEOUT 80 - timeout = jiffies + msecs_to_jiffies(TIMEOUT); - while (!time_after(jiffies, timeout)) { - ret = i2c_master_recv(s->client, buf, 1); - if (ret < 0) { - goto err_mutex_unlock; - } else if (ret != 1) { - ret = -EREMOTEIO; - goto err_mutex_unlock; + if (cmd->rlen) { + /* wait cmd execution terminate */ + #define TIMEOUT 80 + timeout = jiffies + msecs_to_jiffies(TIMEOUT); + while (!time_after(jiffies, timeout)) { + ret = i2c_master_recv(s->client, cmd->args, cmd->rlen); + if (ret < 0) { + goto err_mutex_unlock; + } else if (ret != cmd->rlen) { + ret = -EREMOTEIO; + goto err_mutex_unlock; + } + + /* firmware ready? */ + if ((cmd->args[0] >> 7) & 0x01) + break; } - /* firmware ready? */ - if ((buf[0] >> 7) & 0x01) - break; - } + dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", + __func__, + jiffies_to_msecs(jiffies) - + (jiffies_to_msecs(timeout) - TIMEOUT)); - dev_dbg(&s->client->dev, "%s: cmd execution took %d ms\n", __func__, - jiffies_to_msecs(jiffies) - - (jiffies_to_msecs(timeout) - TIMEOUT)); - - if (!(buf[0] >> 7) & 0x01) { - ret = -ETIMEDOUT; - goto err_mutex_unlock; - } else { - ret = 0; + if (!((cmd->args[0] >> 7) & 0x01)) { + ret = -ETIMEDOUT; + goto err_mutex_unlock; + } } + ret = 0; + err_mutex_unlock: mutex_unlock(&s->i2c_mutex); if (ret) @@ -97,7 +99,8 @@ static int si2157_sleep(struct dvb_frontend *fe) s->active = false; memcpy(cmd.args, "\x13", 1); - cmd.len = 1; + cmd.wlen = 1; + cmd.rlen = 0; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; @@ -141,20 +144,23 @@ static int si2157_set_params(struct dvb_frontend *fe) cmd.args[12] = 0x00; cmd.args[13] = 0x00; cmd.args[14] = 0x01; - cmd.len = 15; + cmd.wlen = 15; + cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; cmd.args[0] = 0x02; - cmd.len = 1; + cmd.wlen = 1; + cmd.rlen = 13; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; cmd.args[0] = 0x01; cmd.args[1] = 0x01; - cmd.len = 2; + cmd.wlen = 2; + cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; @@ -168,7 +174,8 @@ static int si2157_set_params(struct dvb_frontend *fe) cmd.args[5] = (c->frequency >> 8) & 0xff; cmd.args[6] = (c->frequency >> 16) & 0xff; cmd.args[7] = (c->frequency >> 24) & 0xff; - cmd.len = 8; + cmd.wlen = 8; + cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; @@ -212,7 +219,8 @@ static int si2157_probe(struct i2c_client *client, mutex_init(&s->i2c_mutex); /* check if the tuner is there */ - cmd.len = 0; + cmd.wlen = 0; + cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); if (ret) goto err; diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index 6cc6c6fdab7a..6db4c97c26a7 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -31,7 +31,8 @@ struct si2157 { #define SI2157_ARGLEN 30 struct si2157_cmd { u8 args[SI2157_ARGLEN]; - unsigned len; + unsigned wlen; + unsigned rlen; }; #endif -- cgit v1.2.1 From b154121c6470b927794ff7ed17355aedfaa073ca Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Sun, 13 Jul 2014 10:52:19 -0300 Subject: [media] si2157: Move chip initialization to si2157_init Move chip initialization related code from si2157_set_params to si2157_init. Signed-off-by: Olli Salonen Reviewed-by: Antti Palosaari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 71 ++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 41 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index a4908ee81ab9..a92570f944ea 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -80,12 +80,41 @@ err: static int si2157_init(struct dvb_frontend *fe) { struct si2157 *s = fe->tuner_priv; + int ret; + struct si2157_cmd cmd; dev_dbg(&s->client->dev, "%s:\n", __func__); + /* configure? */ + memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); + cmd.wlen = 15; + cmd.rlen = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + + /* query chip revision */ + memcpy(cmd.args, "\x02", 1); + cmd.wlen = 1; + cmd.rlen = 13; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + + /* reboot the tuner with new firmware? */ + memcpy(cmd.args, "\x01\x01", 2); + cmd.wlen = 2; + cmd.rlen = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + s->active = true; return 0; +err: + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); + return ret; } static int si2157_sleep(struct dvb_frontend *fe) @@ -128,48 +157,8 @@ static int si2157_set_params(struct dvb_frontend *fe) goto err; } - /* configure? */ - cmd.args[0] = 0xc0; - cmd.args[1] = 0x00; - cmd.args[2] = 0x0c; - cmd.args[3] = 0x00; - cmd.args[4] = 0x00; - cmd.args[5] = 0x01; - cmd.args[6] = 0x01; - cmd.args[7] = 0x01; - cmd.args[8] = 0x01; - cmd.args[9] = 0x01; - cmd.args[10] = 0x01; - cmd.args[11] = 0x02; - cmd.args[12] = 0x00; - cmd.args[13] = 0x00; - cmd.args[14] = 0x01; - cmd.wlen = 15; - cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); - if (ret) - goto err; - - cmd.args[0] = 0x02; - cmd.wlen = 1; - cmd.rlen = 13; - ret = si2157_cmd_execute(s, &cmd); - if (ret) - goto err; - - cmd.args[0] = 0x01; - cmd.args[1] = 0x01; - cmd.wlen = 2; - cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); - if (ret) - goto err; - /* set frequency */ - cmd.args[0] = 0x41; - cmd.args[1] = 0x00; - cmd.args[2] = 0x00; - cmd.args[3] = 0x00; + memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); cmd.args[4] = (c->frequency >> 0) & 0xff; cmd.args[5] = (c->frequency >> 8) & 0xff; cmd.args[6] = (c->frequency >> 16) & 0xff; -- cgit v1.2.1 From 1b92373f4b845019064d6a7c47b2ba72ebac191c Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Sun, 13 Jul 2014 10:52:20 -0300 Subject: [media] si2157: Add support for Si2158 chip Add support for Si2158 A20 chip. Signed-off-by: Olli Salonen Reviewed-by: Antti Palosaari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 73 +++++++++++++++++++++++++++++++++++--- drivers/media/tuners/si2157.h | 2 +- drivers/media/tuners/si2157_priv.h | 5 ++- 3 files changed, 73 insertions(+), 7 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index a92570f944ea..58c5ef586383 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -1,5 +1,5 @@ /* - * Silicon Labs Si2157 silicon tuner driver + * Silicon Labs Si2157/2158 silicon tuner driver * * Copyright (C) 2014 Antti Palosaari * @@ -16,6 +16,8 @@ #include "si2157_priv.h" +static const struct dvb_tuner_ops si2157_ops; + /* execute firmware command */ static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) { @@ -80,8 +82,11 @@ err: static int si2157_init(struct dvb_frontend *fe) { struct si2157 *s = fe->tuner_priv; - int ret; + int ret, remaining; struct si2157_cmd cmd; + u8 chip, len = 0; + const struct firmware *fw = NULL; + u8 *fw_file; dev_dbg(&s->client->dev, "%s:\n", __func__); @@ -101,6 +106,64 @@ static int si2157_init(struct dvb_frontend *fe) if (ret) goto err; + chip = cmd.args[2]; /* 57 for Si2157, 58 for Si2158 */ + + /* Si2158 requires firmware download */ + if (chip == 58) { + if (((cmd.args[1] & 0x0f) == 1) && (cmd.args[3] == '2') && + (cmd.args[4] == '0')) + fw_file = SI2158_A20_FIRMWARE; + else { + dev_err(&s->client->dev, + "%s: no firmware file for Si%d-%c%c defined\n", + KBUILD_MODNAME, chip, cmd.args[3], cmd.args[4]); + ret = -EINVAL; + goto err; + } + + /* cold state - try to download firmware */ + dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", + KBUILD_MODNAME, si2157_ops.info.name); + + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, &s->client->dev); + if (ret) { + dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", + KBUILD_MODNAME, fw_file); + goto err; + } + + dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", + KBUILD_MODNAME, fw_file); + + /* firmware should be n chunks of 17 bytes */ + if (fw->size % 17 != 0) { + dev_err(&s->client->dev, "%s: firmware file '%s' is invalid\n", + KBUILD_MODNAME, fw_file); + ret = -EINVAL; + goto err; + } + + for (remaining = fw->size; remaining > 0; remaining -= 17) { + memcpy(&len, &fw->data[fw->size - remaining], 1); + memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], + len); + cmd.wlen = len; + cmd.rlen = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) { + dev_err(&s->client->dev, + "%s: firmware download failed=%d\n", + KBUILD_MODNAME, ret); + goto err; + } + } + + release_firmware(fw); + fw = NULL; + + } + /* reboot the tuner with new firmware? */ memcpy(cmd.args, "\x01\x01", 2); cmd.wlen = 2; @@ -177,7 +240,7 @@ err: static const struct dvb_tuner_ops si2157_tuner_ops = { .info = { - .name = "Silicon Labs Si2157", + .name = "Silicon Labs Si2157/Si2158", .frequency_min = 110000000, .frequency_max = 862000000, }, @@ -221,7 +284,7 @@ static int si2157_probe(struct i2c_client *client, i2c_set_clientdata(client, s); dev_info(&s->client->dev, - "%s: Silicon Labs Si2157 successfully attached\n", + "%s: Silicon Labs Si2157/Si2158 successfully attached\n", KBUILD_MODNAME); return 0; err: @@ -263,6 +326,6 @@ static struct i2c_driver si2157_driver = { module_i2c_driver(si2157_driver); -MODULE_DESCRIPTION("Silicon Labs Si2157 silicon tuner driver"); +MODULE_DESCRIPTION("Silicon Labs Si2157/Si2158 silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h index f469a092b66b..4465c46baf0e 100644 --- a/drivers/media/tuners/si2157.h +++ b/drivers/media/tuners/si2157.h @@ -1,5 +1,5 @@ /* - * Silicon Labs Si2157 silicon tuner driver + * Silicon Labs Si2157/2158 silicon tuner driver * * Copyright (C) 2014 Antti Palosaari * diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index 6db4c97c26a7..db79f3c40e4c 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -1,5 +1,5 @@ /* - * Silicon Labs Si2157 silicon tuner driver + * Silicon Labs Si2157/2158 silicon tuner driver * * Copyright (C) 2014 Antti Palosaari * @@ -17,6 +17,7 @@ #ifndef SI2157_PRIV_H #define SI2157_PRIV_H +#include #include "si2157.h" /* state struct */ @@ -35,4 +36,6 @@ struct si2157_cmd { unsigned rlen; }; +#define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw" + #endif -- cgit v1.2.1 From a1dad50d8cdce0be71489c7544ffcd36cf80e994 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Sun, 13 Jul 2014 10:52:21 -0300 Subject: [media] si2157: Set delivery system and bandwidth before tuning Tell used TV standard and bandwidth for tuner firmware. Signed-off-by: Olli Salonen Reviewed-by: Antti Palosaari Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 58c5ef586383..b656f9b72e84 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -209,6 +209,7 @@ static int si2157_set_params(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; struct si2157_cmd cmd; + u8 bandwidth, delivery_system; dev_dbg(&s->client->dev, "%s: delivery_system=%d frequency=%u bandwidth_hz=%u\n", @@ -220,6 +221,36 @@ static int si2157_set_params(struct dvb_frontend *fe) goto err; } + if (c->bandwidth_hz <= 6000000) + bandwidth = 0x06; + else if (c->bandwidth_hz <= 7000000) + bandwidth = 0x07; + else if (c->bandwidth_hz <= 8000000) + bandwidth = 0x08; + else + bandwidth = 0x0f; + + switch (c->delivery_system) { + case SYS_DVBT: + case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */ + delivery_system = 0x20; + break; + case SYS_DVBC_ANNEX_A: + delivery_system = 0x30; + break; + default: + ret = -EINVAL; + goto err; + } + + memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); + cmd.args[4] = delivery_system | bandwidth; + cmd.wlen = 6; + cmd.rlen = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) + goto err; + /* set frequency */ memcpy(cmd.args, "\x41\x00\x00\x00\x00\x00\x00\x00", 8); cmd.args[4] = (c->frequency >> 0) & 0xff; -- cgit v1.2.1 From bac53a2c604779297ac8ee54ce7eda4cc07b65f5 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 13 Jul 2014 16:13:49 -0300 Subject: [media] si2157: advertise Si2158 A20 firmware Driver uses that firmware. Add it module firmware list. Signed-off-by: Antti Palosaari Tested-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index b656f9b72e84..3fa1f268577c 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -360,3 +360,4 @@ module_i2c_driver(si2157_driver); MODULE_DESCRIPTION("Silicon Labs Si2157/Si2158 silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(SI2158_A20_FIRMWARE); -- cgit v1.2.1 From 7d6bc608e062f6b6667c9eeeb17055f017ecadb1 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 13 Jul 2014 20:05:28 -0300 Subject: [media] si2157: rework firmware download logic a little bit Rework firmware selection / chip detection logic a little bit. Add missing release_firmware() to error path. Signed-off-by: Antti Palosaari Tested-by: Olli Salonen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 108 +++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 50 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 3fa1f268577c..329004fbec76 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -82,11 +82,11 @@ err: static int si2157_init(struct dvb_frontend *fe) { struct si2157 *s = fe->tuner_priv; - int ret, remaining; + int ret, len, remaining; struct si2157_cmd cmd; - u8 chip, len = 0; const struct firmware *fw = NULL; u8 *fw_file; + unsigned int chip_id; dev_dbg(&s->client->dev, "%s:\n", __func__); @@ -106,64 +106,69 @@ static int si2157_init(struct dvb_frontend *fe) if (ret) goto err; - chip = cmd.args[2]; /* 57 for Si2157, 58 for Si2158 */ + chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 | + cmd.args[4] << 0; - /* Si2158 requires firmware download */ - if (chip == 58) { - if (((cmd.args[1] & 0x0f) == 1) && (cmd.args[3] == '2') && - (cmd.args[4] == '0')) - fw_file = SI2158_A20_FIRMWARE; - else { - dev_err(&s->client->dev, - "%s: no firmware file for Si%d-%c%c defined\n", - KBUILD_MODNAME, chip, cmd.args[3], cmd.args[4]); - ret = -EINVAL; - goto err; - } + #define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0) + #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) - /* cold state - try to download firmware */ - dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", - KBUILD_MODNAME, si2157_ops.info.name); + switch (chip_id) { + case SI2158_A20: + fw_file = SI2158_A20_FIRMWARE; + break; + case SI2157_A30: + goto skip_fw_download; + break; + default: + dev_err(&s->client->dev, + "%s: unkown chip version Si21%d-%c%c%c\n", + KBUILD_MODNAME, cmd.args[2], cmd.args[1], + cmd.args[3], cmd.args[4]); + ret = -EINVAL; + goto err; + } - /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, &s->client->dev); - if (ret) { - dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", - KBUILD_MODNAME, fw_file); - goto err; - } + /* cold state - try to download firmware */ + dev_info(&s->client->dev, "%s: found a '%s' in cold state\n", + KBUILD_MODNAME, si2157_ops.info.name); - dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", + /* request the firmware, this will block and timeout */ + ret = request_firmware(&fw, fw_file, &s->client->dev); + if (ret) { + dev_err(&s->client->dev, "%s: firmware file '%s' not found\n", KBUILD_MODNAME, fw_file); + goto err; + } - /* firmware should be n chunks of 17 bytes */ - if (fw->size % 17 != 0) { - dev_err(&s->client->dev, "%s: firmware file '%s' is invalid\n", - KBUILD_MODNAME, fw_file); - ret = -EINVAL; - goto err; - } - - for (remaining = fw->size; remaining > 0; remaining -= 17) { - memcpy(&len, &fw->data[fw->size - remaining], 1); - memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], - len); - cmd.wlen = len; - cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); - if (ret) { - dev_err(&s->client->dev, - "%s: firmware download failed=%d\n", - KBUILD_MODNAME, ret); - goto err; - } - } + /* firmware should be n chunks of 17 bytes */ + if (fw->size % 17 != 0) { + dev_err(&s->client->dev, "%s: firmware file '%s' is invalid\n", + KBUILD_MODNAME, fw_file); + ret = -EINVAL; + goto err; + } - release_firmware(fw); - fw = NULL; + dev_info(&s->client->dev, "%s: downloading firmware from file '%s'\n", + KBUILD_MODNAME, fw_file); + for (remaining = fw->size; remaining > 0; remaining -= 17) { + len = fw->data[fw->size - remaining]; + memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); + cmd.wlen = len; + cmd.rlen = 1; + ret = si2157_cmd_execute(s, &cmd); + if (ret) { + dev_err(&s->client->dev, + "%s: firmware download failed=%d\n", + KBUILD_MODNAME, ret); + goto err; + } } + release_firmware(fw); + fw = NULL; + +skip_fw_download: /* reboot the tuner with new firmware? */ memcpy(cmd.args, "\x01\x01", 2); cmd.wlen = 2; @@ -176,6 +181,9 @@ static int si2157_init(struct dvb_frontend *fe) return 0; err: + if (fw) + release_firmware(fw); + dev_dbg(&s->client->dev, "%s: failed=%d\n", __func__, ret); return ret; } -- cgit v1.2.1 From 150dcf5575a937d88ef137f8cddc4f8bd7bb9c8c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 12 Jul 2014 14:53:22 +0300 Subject: msi001: move out of staging Move MSi001 driver from staging to drivers/media/tuners/. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/Kconfig | 6 + drivers/media/tuners/Makefile | 1 + drivers/media/tuners/msi001.c | 500 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 507 insertions(+) create mode 100644 drivers/media/tuners/msi001.c (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 22b6b8bb1d93..906461da1310 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -71,6 +71,12 @@ config MEDIA_TUNER_TEA5767 help Say Y here to include support for the Philips TEA5767 radio tuner. +config MEDIA_TUNER_MSI001 + tristate "Mirics MSi001" + depends on MEDIA_SUPPORT && SPI && VIDEO_V4L2 + help + Mirics MSi001 silicon tuner driver. + config MEDIA_TUNER_MT20XX tristate "Microtune 2032 / 2050 tuners" depends on MEDIA_SUPPORT && I2C diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile index a6ff0c628dfa..5591699755ba 100644 --- a/drivers/media/tuners/Makefile +++ b/drivers/media/tuners/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o +obj-$(CONFIG_MEDIA_TUNER_MSI001) += msi001.o obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o obj-$(CONFIG_MEDIA_TUNER_MT2063) += mt2063.o obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o diff --git a/drivers/media/tuners/msi001.c b/drivers/media/tuners/msi001.c new file mode 100644 index 000000000000..ee99e372c943 --- /dev/null +++ b/drivers/media/tuners/msi001.c @@ -0,0 +1,500 @@ +/* + * Mirics MSi001 silicon tuner driver + * + * Copyright (C) 2013 Antti Palosaari + * Copyright (C) 2014 Antti Palosaari + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +static const struct v4l2_frequency_band bands[] = { + { + .type = V4L2_TUNER_RF, + .index = 0, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 49000000, + .rangehigh = 263000000, + }, { + .type = V4L2_TUNER_RF, + .index = 1, + .capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS, + .rangelow = 390000000, + .rangehigh = 960000000, + }, +}; + +struct msi001 { + struct spi_device *spi; + struct v4l2_subdev sd; + + /* Controls */ + struct v4l2_ctrl_handler hdl; + struct v4l2_ctrl *bandwidth_auto; + struct v4l2_ctrl *bandwidth; + struct v4l2_ctrl *lna_gain; + struct v4l2_ctrl *mixer_gain; + struct v4l2_ctrl *if_gain; + + unsigned int f_tuner; +}; + +static inline struct msi001 *sd_to_msi001(struct v4l2_subdev *sd) +{ + return container_of(sd, struct msi001, sd); +} + +static int msi001_wreg(struct msi001 *s, u32 data) +{ + /* Register format: 4 bits addr + 20 bits value */ + return spi_write(s->spi, &data, 3); +}; + +static int msi001_set_gain(struct msi001 *s, int lna_gain, int mixer_gain, + int if_gain) +{ + int ret; + u32 reg; + dev_dbg(&s->spi->dev, "%s: lna=%d mixer=%d if=%d\n", __func__, + lna_gain, mixer_gain, if_gain); + + reg = 1 << 0; + reg |= (59 - if_gain) << 4; + reg |= 0 << 10; + reg |= (1 - mixer_gain) << 12; + reg |= (1 - lna_gain) << 13; + reg |= 4 << 14; + reg |= 0 << 17; + ret = msi001_wreg(s, reg); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret); + return ret; +}; + +static int msi001_set_tuner(struct msi001 *s) +{ + int ret, i; + unsigned int n, m, thresh, frac, vco_step, tmp, f_if1; + u32 reg; + u64 f_vco, tmp64; + u8 mode, filter_mode, lo_div; + static const struct { + u32 rf; + u8 mode; + u8 lo_div; + } band_lut[] = { + { 50000000, 0xe1, 16}, /* AM_MODE2, antenna 2 */ + {108000000, 0x42, 32}, /* VHF_MODE */ + {330000000, 0x44, 16}, /* B3_MODE */ + {960000000, 0x48, 4}, /* B45_MODE */ + { ~0U, 0x50, 2}, /* BL_MODE */ + }; + static const struct { + u32 freq; + u8 filter_mode; + } if_freq_lut[] = { + { 0, 0x03}, /* Zero IF */ + { 450000, 0x02}, /* 450 kHz IF */ + {1620000, 0x01}, /* 1.62 MHz IF */ + {2048000, 0x00}, /* 2.048 MHz IF */ + }; + static const struct { + u32 freq; + u8 val; + } bandwidth_lut[] = { + { 200000, 0x00}, /* 200 kHz */ + { 300000, 0x01}, /* 300 kHz */ + { 600000, 0x02}, /* 600 kHz */ + {1536000, 0x03}, /* 1.536 MHz */ + {5000000, 0x04}, /* 5 MHz */ + {6000000, 0x05}, /* 6 MHz */ + {7000000, 0x06}, /* 7 MHz */ + {8000000, 0x07}, /* 8 MHz */ + }; + + unsigned int f_rf = s->f_tuner; + + /* + * bandwidth (Hz) + * 200000, 300000, 600000, 1536000, 5000000, 6000000, 7000000, 8000000 + */ + unsigned int bandwidth; + + /* + * intermediate frequency (Hz) + * 0, 450000, 1620000, 2048000 + */ + unsigned int f_if = 0; + #define F_REF 24000000 + #define R_REF 4 + #define F_OUT_STEP 1 + + dev_dbg(&s->spi->dev, + "%s: f_rf=%d f_if=%d\n", + __func__, f_rf, f_if); + + for (i = 0; i < ARRAY_SIZE(band_lut); i++) { + if (f_rf <= band_lut[i].rf) { + mode = band_lut[i].mode; + lo_div = band_lut[i].lo_div; + break; + } + } + + if (i == ARRAY_SIZE(band_lut)) { + ret = -EINVAL; + goto err; + } + + /* AM_MODE is upconverted */ + if ((mode >> 0) & 0x1) + f_if1 = 5 * F_REF; + else + f_if1 = 0; + + for (i = 0; i < ARRAY_SIZE(if_freq_lut); i++) { + if (f_if == if_freq_lut[i].freq) { + filter_mode = if_freq_lut[i].filter_mode; + break; + } + } + + if (i == ARRAY_SIZE(if_freq_lut)) { + ret = -EINVAL; + goto err; + } + + /* filters */ + bandwidth = s->bandwidth->val; + bandwidth = clamp(bandwidth, 200000U, 8000000U); + + for (i = 0; i < ARRAY_SIZE(bandwidth_lut); i++) { + if (bandwidth <= bandwidth_lut[i].freq) { + bandwidth = bandwidth_lut[i].val; + break; + } + } + + if (i == ARRAY_SIZE(bandwidth_lut)) { + ret = -EINVAL; + goto err; + } + + s->bandwidth->val = bandwidth_lut[i].freq; + + dev_dbg(&s->spi->dev, "%s: bandwidth selected=%d\n", + __func__, bandwidth_lut[i].freq); + + f_vco = (u64) (f_rf + f_if + f_if1) * lo_div; + tmp64 = f_vco; + m = do_div(tmp64, F_REF * R_REF); + n = (unsigned int) tmp64; + + vco_step = F_OUT_STEP * lo_div; + thresh = (F_REF * R_REF) / vco_step; + frac = 1ul * thresh * m / (F_REF * R_REF); + + /* Find out greatest common divisor and divide to smaller. */ + tmp = gcd(thresh, frac); + thresh /= tmp; + frac /= tmp; + + /* Force divide to reg max. Resolution will be reduced. */ + tmp = DIV_ROUND_UP(thresh, 4095); + thresh = DIV_ROUND_CLOSEST(thresh, tmp); + frac = DIV_ROUND_CLOSEST(frac, tmp); + + /* calc real RF set */ + tmp = 1ul * F_REF * R_REF * n; + tmp += 1ul * F_REF * R_REF * frac / thresh; + tmp /= lo_div; + + dev_dbg(&s->spi->dev, + "%s: rf=%u:%u n=%d thresh=%d frac=%d\n", + __func__, f_rf, tmp, n, thresh, frac); + + ret = msi001_wreg(s, 0x00000e); + if (ret) + goto err; + + ret = msi001_wreg(s, 0x000003); + if (ret) + goto err; + + reg = 0 << 0; + reg |= mode << 4; + reg |= filter_mode << 12; + reg |= bandwidth << 14; + reg |= 0x02 << 17; + reg |= 0x00 << 20; + ret = msi001_wreg(s, reg); + if (ret) + goto err; + + reg = 5 << 0; + reg |= thresh << 4; + reg |= 1 << 19; + reg |= 1 << 21; + ret = msi001_wreg(s, reg); + if (ret) + goto err; + + reg = 2 << 0; + reg |= frac << 4; + reg |= n << 16; + ret = msi001_wreg(s, reg); + if (ret) + goto err; + + ret = msi001_set_gain(s, s->lna_gain->cur.val, s->mixer_gain->cur.val, + s->if_gain->cur.val); + if (ret) + goto err; + + reg = 6 << 0; + reg |= 63 << 4; + reg |= 4095 << 10; + ret = msi001_wreg(s, reg); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&s->spi->dev, "%s: failed %d\n", __func__, ret); + return ret; +}; + +static int msi001_s_power(struct v4l2_subdev *sd, int on) +{ + struct msi001 *s = sd_to_msi001(sd); + int ret; + dev_dbg(&s->spi->dev, "%s: on=%d\n", __func__, on); + + if (on) + ret = 0; + else + ret = msi001_wreg(s, 0x000000); + + return ret; +} + +static const struct v4l2_subdev_core_ops msi001_core_ops = { + .s_power = msi001_s_power, +}; + +static int msi001_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) +{ + struct msi001 *s = sd_to_msi001(sd); + dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index); + + strlcpy(v->name, "Mirics MSi001", sizeof(v->name)); + v->type = V4L2_TUNER_RF; + v->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; + v->rangelow = 49000000; + v->rangehigh = 960000000; + + return 0; +} + +static int msi001_s_tuner(struct v4l2_subdev *sd, const struct v4l2_tuner *v) +{ + struct msi001 *s = sd_to_msi001(sd); + dev_dbg(&s->spi->dev, "%s: index=%d\n", __func__, v->index); + return 0; +} + +static int msi001_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct msi001 *s = sd_to_msi001(sd); + dev_dbg(&s->spi->dev, "%s: tuner=%d\n", __func__, f->tuner); + f->frequency = s->f_tuner; + return 0; +} + +static int msi001_s_frequency(struct v4l2_subdev *sd, + const struct v4l2_frequency *f) +{ + struct msi001 *s = sd_to_msi001(sd); + unsigned int band; + dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d frequency=%u\n", + __func__, f->tuner, f->type, f->frequency); + + if (f->frequency < ((bands[0].rangehigh + bands[1].rangelow) / 2)) + band = 0; + else + band = 1; + s->f_tuner = clamp_t(unsigned int, f->frequency, + bands[band].rangelow, bands[band].rangehigh); + + return msi001_set_tuner(s); +} + +static int msi001_enum_freq_bands(struct v4l2_subdev *sd, + struct v4l2_frequency_band *band) +{ + struct msi001 *s = sd_to_msi001(sd); + dev_dbg(&s->spi->dev, "%s: tuner=%d type=%d index=%d\n", + __func__, band->tuner, band->type, band->index); + + if (band->index >= ARRAY_SIZE(bands)) + return -EINVAL; + + band->capability = bands[band->index].capability; + band->rangelow = bands[band->index].rangelow; + band->rangehigh = bands[band->index].rangehigh; + + return 0; +} + +static const struct v4l2_subdev_tuner_ops msi001_tuner_ops = { + .g_tuner = msi001_g_tuner, + .s_tuner = msi001_s_tuner, + .g_frequency = msi001_g_frequency, + .s_frequency = msi001_s_frequency, + .enum_freq_bands = msi001_enum_freq_bands, +}; + +static const struct v4l2_subdev_ops msi001_ops = { + .core = &msi001_core_ops, + .tuner = &msi001_tuner_ops, +}; + +static int msi001_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct msi001 *s = container_of(ctrl->handler, struct msi001, hdl); + + int ret; + dev_dbg(&s->spi->dev, + "%s: id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", + __func__, ctrl->id, ctrl->name, ctrl->val, + ctrl->minimum, ctrl->maximum, ctrl->step); + + switch (ctrl->id) { + case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: + case V4L2_CID_RF_TUNER_BANDWIDTH: + ret = msi001_set_tuner(s); + break; + case V4L2_CID_RF_TUNER_LNA_GAIN: + ret = msi001_set_gain(s, s->lna_gain->val, + s->mixer_gain->cur.val, s->if_gain->cur.val); + break; + case V4L2_CID_RF_TUNER_MIXER_GAIN: + ret = msi001_set_gain(s, s->lna_gain->cur.val, + s->mixer_gain->val, s->if_gain->cur.val); + break; + case V4L2_CID_RF_TUNER_IF_GAIN: + ret = msi001_set_gain(s, s->lna_gain->cur.val, + s->mixer_gain->cur.val, s->if_gain->val); + break; + default: + dev_dbg(&s->spi->dev, "%s: unkown control %d\n", + __func__, ctrl->id); + ret = -EINVAL; + } + + return ret; +} + +static const struct v4l2_ctrl_ops msi001_ctrl_ops = { + .s_ctrl = msi001_s_ctrl, +}; + +static int msi001_probe(struct spi_device *spi) +{ + struct msi001 *s; + int ret; + dev_dbg(&spi->dev, "%s:\n", __func__); + + s = kzalloc(sizeof(struct msi001), GFP_KERNEL); + if (s == NULL) { + ret = -ENOMEM; + dev_dbg(&spi->dev, "Could not allocate memory for msi001\n"); + goto err_kfree; + } + + s->spi = spi; + s->f_tuner = bands[0].rangelow; + v4l2_spi_subdev_init(&s->sd, spi, &msi001_ops); + + /* Register controls */ + v4l2_ctrl_handler_init(&s->hdl, 5); + s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, 0, 1, 1, 1); + s->bandwidth = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + V4L2_CID_RF_TUNER_BANDWIDTH, 200000, 8000000, 1, 200000); + v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); + s->lna_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + V4L2_CID_RF_TUNER_LNA_GAIN, 0, 1, 1, 1); + s->mixer_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + V4L2_CID_RF_TUNER_MIXER_GAIN, 0, 1, 1, 1); + s->if_gain = v4l2_ctrl_new_std(&s->hdl, &msi001_ctrl_ops, + V4L2_CID_RF_TUNER_IF_GAIN, 0, 59, 1, 0); + if (s->hdl.error) { + ret = s->hdl.error; + dev_err(&s->spi->dev, "Could not initialize controls\n"); + /* control init failed, free handler */ + goto err_ctrl_handler_free; + } + + s->sd.ctrl_handler = &s->hdl; + return 0; + +err_ctrl_handler_free: + v4l2_ctrl_handler_free(&s->hdl); +err_kfree: + kfree(s); + return ret; +} + +static int msi001_remove(struct spi_device *spi) +{ + struct v4l2_subdev *sd = spi_get_drvdata(spi); + struct msi001 *s = sd_to_msi001(sd); + dev_dbg(&spi->dev, "%s:\n", __func__); + + /* + * Registered by v4l2_spi_new_subdev() from master driver, but we must + * unregister it from here. Weird. + */ + v4l2_device_unregister_subdev(&s->sd); + v4l2_ctrl_handler_free(&s->hdl); + kfree(s); + return 0; +} + +static const struct spi_device_id msi001_id[] = { + {"msi001", 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, msi001_id); + +static struct spi_driver msi001_driver = { + .driver = { + .name = "msi001", + .owner = THIS_MODULE, + }, + .probe = msi001_probe, + .remove = msi001_remove, + .id_table = msi001_id, +}; +module_spi_driver(msi001_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Mirics MSi001"); +MODULE_LICENSE("GPL"); -- cgit v1.2.1 From 6cc8a35dcf6bceb4c6db38ae7862d826b3afb6a2 Mon Sep 17 00:00:00 2001 From: Olli Salonen Date: Fri, 18 Jul 2014 02:41:12 -0300 Subject: [media] si2157: Use name si2157_ops instead of si2157_tuner_ops The struct prototype is defined at the beginning of the code as "si2157_ops" but the real struct is called "si2157_tuner_ops". This is causing the name to be empty on this info msg: si2157 16-0060: si2157: found a '' in cold state [crope@iki.fi: commit msg from Luis email reply] Signed-off-by: Olli Salonen Cc: Luis Alves Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 329004fbec76..4730f69cac01 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -277,7 +277,7 @@ err: return ret; } -static const struct dvb_tuner_ops si2157_tuner_ops = { +static const struct dvb_tuner_ops si2157_ops = { .info = { .name = "Silicon Labs Si2157/Si2158", .frequency_min = 110000000, @@ -317,7 +317,7 @@ static int si2157_probe(struct i2c_client *client, goto err; fe->tuner_priv = s; - memcpy(&fe->ops.tuner_ops, &si2157_tuner_ops, + memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops)); i2c_set_clientdata(client, s); -- cgit v1.2.1 From 05024efe1264c3379135d7223f2c84c8f7ef2172 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 15 Jul 2014 16:34:36 -0300 Subject: [media] si2157: Add support for spectral inversion This is needed for PCTV 522e support. Signed-off-by: Matthias Schwarzott Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 3 +++ drivers/media/tuners/si2157.h | 5 +++++ drivers/media/tuners/si2157_priv.h | 1 + 3 files changed, 9 insertions(+) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 4730f69cac01..f6199832b4cc 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -253,6 +253,8 @@ static int si2157_set_params(struct dvb_frontend *fe) memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); cmd.args[4] = delivery_system | bandwidth; + if (s->inversion) + cmd.args[5] = 0x01; cmd.wlen = 6; cmd.rlen = 1; ret = si2157_cmd_execute(s, &cmd); @@ -307,6 +309,7 @@ static int si2157_probe(struct i2c_client *client, s->client = client; s->fe = cfg->fe; + s->inversion = cfg->inversion; mutex_init(&s->i2c_mutex); /* check if the tuner is there */ diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h index 4465c46baf0e..6da4d5d1c817 100644 --- a/drivers/media/tuners/si2157.h +++ b/drivers/media/tuners/si2157.h @@ -29,6 +29,11 @@ struct si2157_config { * frontend */ struct dvb_frontend *fe; + + /* + * Spectral Inversion + */ + bool inversion; }; #endif diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index db79f3c40e4c..3ddab5e6b500 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -26,6 +26,7 @@ struct si2157 { struct i2c_client *client; struct dvb_frontend *fe; bool active; + bool inversion; }; /* firmare command struct */ -- cgit v1.2.1 From 1140540da0e4b00978607244b58f06a528b819c7 Mon Sep 17 00:00:00 2001 From: Matthias Schwarzott Date: Tue, 15 Jul 2014 04:58:40 -0300 Subject: [media] si2157: Add get_if_frequency callback This is needed for PCTV 522e support. Signed-off-by: Matthias Schwarzott Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/si2157.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index f6199832b4cc..6c53edb73a63 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -279,6 +279,12 @@ err: return ret; } +static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 5000000; /* default value of property 0x0706 */ + return 0; +} + static const struct dvb_tuner_ops si2157_ops = { .info = { .name = "Silicon Labs Si2157/Si2158", @@ -289,6 +295,7 @@ static const struct dvb_tuner_ops si2157_ops = { .init = si2157_init, .sleep = si2157_sleep, .set_params = si2157_set_params, + .get_if_frequency = si2157_get_if_frequency, }; static int si2157_probe(struct i2c_client *client, -- cgit v1.2.1 From e23cf7f358554139a7c9f0d2739eec187599c670 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jul 2014 14:22:01 -0300 Subject: [media] tuners/Kconfig: fix build when just DTV or SDR is enabled As reported by Kbuildtest: warning: (VIDEO_PVRUSB2 && VIDEO_TLG2300 && VIDEO_USBVISION && VIDEO_GO7007 && VIDEO_AU0828_V4L2 && VIDEO_CX231XX && VIDEO_TM6000 && VIDEO_EM28XX && VIDEO_IVTV && VIDEO_MXB && VIDEO_CX18 && VIDEO_CX23885 && VIDEO_CX88 && VIDEO_BT848 && VIDEO_SAA7134 && VIDEO_SAA7164) selects VIDEO_TUNER which has unmet direct dependencies (MEDIA_SUPPORT && MEDIA_TUNER) That happens when: # CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y # CONFIG_MEDIA_RADIO_SUPPORT is not set # CONFIG_MEDIA_SDR_SUPPORT is not set CONFIG_VIDEO_AU0828_V4L2=y CONFIG_VIDEO_CX231XX=y CONFIG_VIDEO_TM6000=y CONFIG_VIDEO_EM28XX=y CONFIG_VIDEO_TUNER=y CONFIG_MEDIA_SUPPORT=y With means that we need to enable MEDIA_TUNER also when DTV is enabled. While the above config doesn't cover, if we enable SDR, the same error can also happen. Reported-by: kbuild test robot Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 906461da1310..51edd101f250 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -1,7 +1,7 @@ # Analog TV tuners, auto-loaded via tuner.ko config MEDIA_TUNER tristate - depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT) && I2C + depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT) && I2C default y select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT -- cgit v1.2.1 From d616bec6b970632abfb117bae9e16a0d7db8d46d Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 8 Jul 2014 14:20:37 -0300 Subject: [media] r820t: remove unnecessary break after goto Cc: Antti Palosaari Cc: Michael Krufky Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/r820t.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index a0db64f57a98..a759742cae7b 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -2300,7 +2300,6 @@ struct dvb_frontend *r820t_attach(struct dvb_frontend *fe, case 0: /* memory allocation failure */ goto err_no_gate; - break; case 1: /* new tuner instance */ priv->cfg = cfg; -- cgit v1.2.1 From 435fce1b6cdd53a906d85a4416d89dfab1986043 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 8 Jul 2014 14:25:17 -0300 Subject: [media] xc2028: remove unnecessary break after goto Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tuner-xc2028.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 6ef93ee1fdcb..565eeebb3aeb 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -1489,7 +1489,6 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, case 0: /* memory allocation failure */ goto fail; - break; case 1: /* new tuner instance */ priv->ctrl.max_len = 13; -- cgit v1.2.1 From 2442b6afd0911453f0bf412af4b420439861503d Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 8 Jul 2014 15:17:41 -0300 Subject: [media] xc5000: remove unnecessary break after goto Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc5000.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index 2b3d514be672..f059ba2f5656 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -1302,7 +1302,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, switch (instance) { case 0: goto fail; - break; case 1: /* new tuner instance */ priv->bandwidth = 6000000; -- cgit v1.2.1 From 5cb4e2276ccfc9fc1b9fdcf99d274dc199f153e3 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 8 Jul 2014 15:16:51 -0300 Subject: [media] xc4000: remove unnecessary break after goto Signed-off-by: Fabian Frederick Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc4000.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 2018befabb5a..d2ee5dfc1a70 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -1668,7 +1668,6 @@ struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe, switch (instance) { case 0: goto fail; - break; case 1: /* new tuner instance */ priv->bandwidth = 6000000; -- cgit v1.2.1 From da7bfa2c5df3221c7b63be05d5110e21a11118dc Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Jul 2014 13:28:13 -0300 Subject: [media] xc4000: Update firmware name The firmware name at: http://www.kernellabs.com/firmware/xc4000/ Is different from the one at the Kernel. Update it try first the new name, falling back to the previous one if the new name can't be found. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc4000.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index d2ee5dfc1a70..5fb2f110245a 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -116,6 +116,7 @@ struct xc4000_priv { #define XC4000_AUDIO_STD_MONO 32 #define XC4000_DEFAULT_FIRMWARE "dvb-fe-xc4000-1.4.fw" +#define XC4000_DEFAULT_FIRMWARE_NEW "dvb-fe-xc4000-1.4.1.fw" /* Misc Defines */ #define MAX_TV_STANDARD 24 @@ -730,13 +731,25 @@ static int xc4000_fwupload(struct dvb_frontend *fe) char name[33]; const char *fname; - if (firmware_name[0] != '\0') + if (firmware_name[0] != '\0') { fname = firmware_name; - else - fname = XC4000_DEFAULT_FIRMWARE; - dprintk(1, "Reading firmware %s\n", fname); - rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent); + dprintk(1, "Reading custom firmware %s\n", fname); + rc = request_firmware(&fw, fname, + priv->i2c_props.adap->dev.parent); + } else { + fname = XC4000_DEFAULT_FIRMWARE_NEW; + dprintk(1, "Trying to read firmware %s\n", fname); + rc = request_firmware(&fw, fname, + priv->i2c_props.adap->dev.parent); + if (rc == -ENOENT) { + fname = XC4000_DEFAULT_FIRMWARE; + dprintk(1, "Trying to read firmware %s\n", fname); + rc = request_firmware(&fw, fname, + priv->i2c_props.adap->dev.parent); + } + } + if (rc < 0) { if (rc == -ENOENT) printk(KERN_ERR "Error: firmware %s not found.\n", fname); @@ -746,6 +759,8 @@ static int xc4000_fwupload(struct dvb_frontend *fe) return rc; } + dprintk(1, "Loading Firmware: %s\n", fname); + p = fw->data; endp = p + fw->size; -- cgit v1.2.1 From 612ae142ac02e8b187f23b8c9f1cc9cd8c6a078d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Jul 2014 13:28:14 -0300 Subject: [media] xc4000: add module meta-tag with the firmware names This meta-tag is used by some distros to help them package the firmware and generate proper initrd images. So, add the firmware names there. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc4000.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 5fb2f110245a..17a936ea9325 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -1769,3 +1769,5 @@ EXPORT_SYMBOL(xc4000_attach); MODULE_AUTHOR("Steven Toth, Davide Ferri"); MODULE_DESCRIPTION("Xceive xc4000 silicon tuner driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(XC4000_DEFAULT_FIRMWARE_NEW); +MODULE_FIRMWARE(XC4000_DEFAULT_FIRMWARE); -- cgit v1.2.1 From a3eec916cbc17dc1aaa3ddf120836cd5200eb4ef Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Jul 2014 14:21:18 -0300 Subject: [media] xc5000: Fix get_frequency() The programmed frequency on xc5000 is not the middle frequency, but the initial frequency on the bandwidth range. However, the DVB API works with the middle frequency. Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc5000.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index f059ba2f5656..ab7d444bcdcc 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -56,7 +56,7 @@ struct xc5000_priv { u32 if_khz; u16 xtal_khz; - u32 freq_hz; + u32 freq_hz, freq_offset; u32 bandwidth; u8 video_standard; u8 rf_mode; @@ -749,13 +749,13 @@ static int xc5000_set_params(struct dvb_frontend *fe) case SYS_ATSC: dprintk(1, "%s() VSB modulation\n", __func__); priv->rf_mode = XC_RF_MODE_AIR; - priv->freq_hz = freq - 1750000; + priv->freq_offset = 1750000; priv->video_standard = DTV6; break; case SYS_DVBC_ANNEX_B: dprintk(1, "%s() QAM modulation\n", __func__); priv->rf_mode = XC_RF_MODE_CABLE; - priv->freq_hz = freq - 1750000; + priv->freq_offset = 1750000; priv->video_standard = DTV6; break; case SYS_ISDBT: @@ -770,15 +770,15 @@ static int xc5000_set_params(struct dvb_frontend *fe) switch (bw) { case 6000000: priv->video_standard = DTV6; - priv->freq_hz = freq - 1750000; + priv->freq_offset = 1750000; break; case 7000000: priv->video_standard = DTV7; - priv->freq_hz = freq - 2250000; + priv->freq_offset = 2250000; break; case 8000000: priv->video_standard = DTV8; - priv->freq_hz = freq - 2750000; + priv->freq_offset = 2750000; break; default: printk(KERN_ERR "xc5000 bandwidth not set!\n"); @@ -792,15 +792,15 @@ static int xc5000_set_params(struct dvb_frontend *fe) priv->rf_mode = XC_RF_MODE_CABLE; if (bw <= 6000000) { priv->video_standard = DTV6; - priv->freq_hz = freq - 1750000; + priv->freq_offset = 1750000; b = 6; } else if (bw <= 7000000) { priv->video_standard = DTV7; - priv->freq_hz = freq - 2250000; + priv->freq_offset = 2250000; b = 7; } else { priv->video_standard = DTV7_8; - priv->freq_hz = freq - 2750000; + priv->freq_offset = 2750000; b = 8; } dprintk(1, "%s() Bandwidth %dMHz (%d)\n", __func__, @@ -811,6 +811,8 @@ static int xc5000_set_params(struct dvb_frontend *fe) return -EINVAL; } + priv->freq_hz = freq - priv->freq_offset; + dprintk(1, "%s() frequency=%d (compensated to %d)\n", __func__, freq, priv->freq_hz); @@ -1061,7 +1063,7 @@ static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq) { struct xc5000_priv *priv = fe->tuner_priv; dprintk(1, "%s()\n", __func__); - *freq = priv->freq_hz; + *freq = priv->freq_hz + priv->freq_offset; return 0; } -- cgit v1.2.1 From 4c07e32884ab69574cfd9eb4de3334233c938071 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 21 Jul 2014 13:28:15 -0300 Subject: [media] xc4000: Fix get_frequency() The programmed frequency on xc4000 is not the middle frequency, but the initial frequency on the bandwidth range. However, the DVB API works with the middle frequency. This works fine on set_frontend, as the device calculates the needed offset. However, at get_frequency(), the returned value is the initial frequency. That's generally not a big problem on most drivers, however, starting with changeset 6fe1099c7aec, the frequency drift is taken into account at dib7000p driver. This broke support for PCTV 340e, with uses dib7000p demod and xc4000 tuner. Cc: stable@vger.kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc4000.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 17a936ea9325..f9ab79e3432d 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -93,7 +93,7 @@ struct xc4000_priv { struct firmware_description *firm; int firm_size; u32 if_khz; - u32 freq_hz; + u32 freq_hz, freq_offset; u32 bandwidth; u8 video_standard; u8 rf_mode; @@ -1172,14 +1172,14 @@ static int xc4000_set_params(struct dvb_frontend *fe) case SYS_ATSC: dprintk(1, "%s() VSB modulation\n", __func__); priv->rf_mode = XC_RF_MODE_AIR; - priv->freq_hz = c->frequency - 1750000; + priv->freq_offset = 1750000; priv->video_standard = XC4000_DTV6; type = DTV6; break; case SYS_DVBC_ANNEX_B: dprintk(1, "%s() QAM modulation\n", __func__); priv->rf_mode = XC_RF_MODE_CABLE; - priv->freq_hz = c->frequency - 1750000; + priv->freq_offset = 1750000; priv->video_standard = XC4000_DTV6; type = DTV6; break; @@ -1188,23 +1188,23 @@ static int xc4000_set_params(struct dvb_frontend *fe) dprintk(1, "%s() OFDM\n", __func__); if (bw == 0) { if (c->frequency < 400000000) { - priv->freq_hz = c->frequency - 2250000; + priv->freq_offset = 2250000; } else { - priv->freq_hz = c->frequency - 2750000; + priv->freq_offset = 2750000; } priv->video_standard = XC4000_DTV7_8; type = DTV78; } else if (bw <= 6000000) { priv->video_standard = XC4000_DTV6; - priv->freq_hz = c->frequency - 1750000; + priv->freq_offset = 1750000; type = DTV6; } else if (bw <= 7000000) { priv->video_standard = XC4000_DTV7; - priv->freq_hz = c->frequency - 2250000; + priv->freq_offset = 2250000; type = DTV7; } else { priv->video_standard = XC4000_DTV8; - priv->freq_hz = c->frequency - 2750000; + priv->freq_offset = 2750000; type = DTV8; } priv->rf_mode = XC_RF_MODE_AIR; @@ -1215,6 +1215,8 @@ static int xc4000_set_params(struct dvb_frontend *fe) goto fail; } + priv->freq_hz = c->frequency - priv->freq_offset; + dprintk(1, "%s() frequency=%d (compensated)\n", __func__, priv->freq_hz); @@ -1535,7 +1537,7 @@ static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq) { struct xc4000_priv *priv = fe->tuner_priv; - *freq = priv->freq_hz; + *freq = priv->freq_hz + priv->freq_offset; if (debug) { mutex_lock(&priv->lock); -- cgit v1.2.1 From f5b44da1ac4146e06147a5df3058f4c265c932ec Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 25 Jul 2014 06:17:59 -0300 Subject: [media] Kconfig: fix tuners build warnings [next:master 7472/8702] warning: (USB_MSI2500) selects MEDIA_TUNER_MSI001 which has unmet direct dependencies ((MEDIA_ANALOG_TV_SUPPORT || ..) && ..) [next:master 7698/8702] warning: (MEDIA_TUNER && ..) selects MEDIA_TUNER_XC5000 which has unmet direct dependencies ((MEDIA_ANALOG_TV_SUPPORT || ..) && ..) Reported-by: kbuild test robot Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig index 51edd101f250..d79fd1ce5a18 100644 --- a/drivers/media/tuners/Kconfig +++ b/drivers/media/tuners/Kconfig @@ -16,7 +16,7 @@ config MEDIA_TUNER menu "Customize TV tuners" visible if !MEDIA_SUBDRV_AUTOSELECT - depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT + depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT || MEDIA_SDR_SUPPORT config MEDIA_TUNER_SIMPLE tristate "Simple tuner support" @@ -74,6 +74,7 @@ config MEDIA_TUNER_TEA5767 config MEDIA_TUNER_MSI001 tristate "Mirics MSi001" depends on MEDIA_SUPPORT && SPI && VIDEO_V4L2 + default m if !MEDIA_SUBDRV_AUTOSELECT help Mirics MSi001 silicon tuner driver. -- cgit v1.2.1 From 2621c0b3224633393ef9e24c3ac9de2770b3750c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 30 Jul 2014 10:36:32 -0300 Subject: [media] xc5000: Don't try forever to load the firmware With the current code, if something bad happens during the firmware init process, the device will keep trying forever, and removing it would cause an OOPS. Instead, try only a limited amount of time. If not, fails. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc5000.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index ab7d444bcdcc..460df44aa1ad 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -1101,16 +1101,17 @@ static int xc5000_get_status(struct dvb_frontend *fe, u32 *status) static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) { struct xc5000_priv *priv = fe->tuner_priv; - int ret = 0; + int ret, i; u16 pll_lock_status; u16 fw_ck; cancel_delayed_work(&priv->timer_sleep); - if (force || xc5000_is_firmware_loaded(fe) != 0) { - -fw_retry: + if (!force && xc5000_is_firmware_loaded(fe) == 0) + return 0; + /* Try up to 5 times to load firmware */ + for (i = 0; i < 5; i++) { ret = xc5000_fwupload(fe); if (ret != 0) return ret; @@ -1118,25 +1119,25 @@ fw_retry: msleep(20); if (priv->fw_checksum_supported) { - if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck) - != 0) { + if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck)) { dprintk(1, "%s() FW checksum reading failed.\n", __func__); - goto fw_retry; + continue; } - if (fw_ck == 0) { + if (!fw_ck) { dprintk(1, "%s() FW checksum failed = 0x%04x\n", __func__, fw_ck); - goto fw_retry; + continue; } } /* Start the tuner self-calibration process */ - ret |= xc_initialize(priv); - - if (ret != 0) - goto fw_retry; + ret = xc_initialize(priv); + if (ret) { + dprintk(1, "Can't request Self-callibration. Reloading firmware\n"); + continue; + } /* Wait for calibration to complete. * We could continue but XC5000 will clock stretch subsequent @@ -1146,15 +1147,15 @@ fw_retry: msleep(100); if (priv->init_status_supported) { - if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != 0) { + if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck)) { dprintk(1, "%s() FW failed reading init status.\n", __func__); - goto fw_retry; + continue; } - if (fw_ck == 0) { + if (!fw_ck) { dprintk(1, "%s() FW init status failed = 0x%04x\n", __func__, fw_ck); - goto fw_retry; + continue; } } @@ -1164,12 +1165,13 @@ fw_retry: if (pll_lock_status > 63) { /* PLL is unlocked, force reload of the firmware */ printk(KERN_ERR "xc5000: PLL not running after fwload.\n"); - goto fw_retry; + continue; } } /* Default to "CABLE" mode */ - ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE); + ret = xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE); + break; } return ret; -- cgit v1.2.1 From 8604f3552b1c995186acc8306acf25ee73fd9d5e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 30 Jul 2014 11:09:15 -0300 Subject: [media] xc5000: optimize firmware retry logic Currently, firmware retry logic keeps reading from FS every time during the retry logic. This is not needed. Instead, only release the firmware read after success. While here, make the non-debug messages less verbose, as it only matters to the user if the firmware was successfully loaded, or if some error happened. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc5000.c | 73 ++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 35 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index 460df44aa1ad..39f9fb1001d8 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -625,48 +625,30 @@ static int xc_set_xtal(struct dvb_frontend *fe) return ret; } -static int xc5000_fwupload(struct dvb_frontend *fe) +static int xc5000_fwupload(struct dvb_frontend *fe, + const struct xc5000_fw_cfg *desired_fw, + const struct firmware *fw) { struct xc5000_priv *priv = fe->tuner_priv; - const struct firmware *fw; int ret; - const struct xc5000_fw_cfg *desired_fw = - xc5000_assign_firmware(priv->chip_id); - priv->pll_register_no = desired_fw->pll_reg; - priv->init_status_supported = desired_fw->init_status_supported; - priv->fw_checksum_supported = desired_fw->fw_checksum_supported; /* request the firmware, this will block and timeout */ - printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n", + dprintk(1, "waiting for firmware upload (%s)...\n", desired_fw->name); - ret = request_firmware(&fw, desired_fw->name, - priv->i2c_props.adap->dev.parent); - if (ret) { - printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); - goto out; - } else { - printk(KERN_DEBUG "xc5000: firmware read %Zu bytes.\n", - fw->size); - ret = 0; - } + priv->pll_register_no = desired_fw->pll_reg; + priv->init_status_supported = desired_fw->init_status_supported; + priv->fw_checksum_supported = desired_fw->fw_checksum_supported; - if (fw->size != desired_fw->size) { - printk(KERN_ERR "xc5000: firmware incorrect size\n"); - ret = -EINVAL; - } else { - printk(KERN_INFO "xc5000: firmware uploading...\n"); - ret = xc_load_i2c_sequence(fe, fw->data); - if (0 == ret) - ret = xc_set_xtal(fe); - if (0 == ret) - printk(KERN_INFO "xc5000: firmware upload complete...\n"); - else - printk(KERN_ERR "xc5000: firmware upload failed...\n"); - } -out: - release_firmware(fw); + dprintk(1, "firmware uploading...\n"); + ret = xc_load_i2c_sequence(fe, fw->data); + if (!ret) { + ret = xc_set_xtal(fe); + dprintk(1, "Firmware upload complete...\n"); + } else + printk(KERN_ERR "xc5000: firmware upload failed...\n"); + return ret; } @@ -1101,6 +1083,8 @@ static int xc5000_get_status(struct dvb_frontend *fe, u32 *status) static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) { struct xc5000_priv *priv = fe->tuner_priv; + const struct xc5000_fw_cfg *desired_fw = xc5000_assign_firmware(priv->chip_id); + const struct firmware *fw; int ret, i; u16 pll_lock_status; u16 fw_ck; @@ -1110,11 +1094,26 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) if (!force && xc5000_is_firmware_loaded(fe) == 0) return 0; + ret = request_firmware(&fw, desired_fw->name, + priv->i2c_props.adap->dev.parent); + if (ret) { + printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); + return ret; + } + + dprintk(1, "firmware read %Zu bytes.\n", fw->size); + + if (fw->size != desired_fw->size) { + printk(KERN_ERR "xc5000: firmware file with incorrect size\n"); + ret = -EINVAL; + goto err; + } + /* Try up to 5 times to load firmware */ for (i = 0; i < 5; i++) { - ret = xc5000_fwupload(fe); + ret = xc5000_fwupload(fe, desired_fw, fw); if (ret != 0) - return ret; + goto err; msleep(20); @@ -1171,9 +1170,13 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) /* Default to "CABLE" mode */ ret = xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE); + printk(KERN_INFO "xc5000: Firmware %s loaded and running.\n", + desired_fw->name); break; } +err: + release_firmware(fw); return ret; } -- cgit v1.2.1 From ee67674a7758dddc6abad16182b832a6a13b9ef9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 30 Jul 2014 11:17:46 -0300 Subject: [media] xc5000: always write at dmesg when it fails to upload firmware On a normal condition, no errors should happen at xc5000 firmware upload is done. So, print at the dmesg if something bad happens. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc5000.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'drivers/media/tuners') diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index 39f9fb1001d8..e135760f7d48 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -1104,13 +1104,16 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) dprintk(1, "firmware read %Zu bytes.\n", fw->size); if (fw->size != desired_fw->size) { - printk(KERN_ERR "xc5000: firmware file with incorrect size\n"); + printk(KERN_ERR "xc5000: Firmware file with incorrect size\n"); ret = -EINVAL; goto err; } /* Try up to 5 times to load firmware */ for (i = 0; i < 5; i++) { + if (i) + printk(KERN_CONT " - retrying to upload firmware.\n"); + ret = xc5000_fwupload(fe, desired_fw, fw); if (ret != 0) goto err; @@ -1119,14 +1122,15 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) if (priv->fw_checksum_supported) { if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck)) { - dprintk(1, "%s() FW checksum reading failed.\n", - __func__); + printk(KERN_ERR + "xc5000: FW checksum reading failed."); continue; } if (!fw_ck) { - dprintk(1, "%s() FW checksum failed = 0x%04x\n", - __func__, fw_ck); + printk(KERN_ERR + "xc5000: FW checksum failed = 0x%04x.", + fw_ck); continue; } } @@ -1134,7 +1138,8 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) /* Start the tuner self-calibration process */ ret = xc_initialize(priv); if (ret) { - dprintk(1, "Can't request Self-callibration. Reloading firmware\n"); + printk(KERN_ERR + "xc5000: Can't request Self-callibration."); continue; } @@ -1147,13 +1152,15 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) if (priv->init_status_supported) { if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck)) { - dprintk(1, "%s() FW failed reading init status.\n", - __func__); + printk(KERN_ERR + "xc5000: FW failed reading init status."); continue; } if (!fw_ck) { - dprintk(1, "%s() FW init status failed = 0x%04x\n", __func__, fw_ck); + printk(KERN_ERR + "xc5000: FW init status failed = 0x%04x.", + fw_ck); continue; } } @@ -1163,19 +1170,26 @@ static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force) &pll_lock_status); if (pll_lock_status > 63) { /* PLL is unlocked, force reload of the firmware */ - printk(KERN_ERR "xc5000: PLL not running after fwload.\n"); + printk(KERN_ERR + "xc5000: PLL not running after fwload."); continue; } } /* Default to "CABLE" mode */ ret = xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE); - printk(KERN_INFO "xc5000: Firmware %s loaded and running.\n", - desired_fw->name); - break; + if (!ret) + break; + printk(KERN_ERR "xc5000: can't set to cable mode."); } err: + if (!ret) + printk(KERN_INFO "xc5000: Firmware %s loaded and running.\n", + desired_fw->name); + else + printk(KERN_CONT " - too many retries. Giving up\n"); + release_firmware(fw); return ret; } -- cgit v1.2.1