@@ -388,7 +388,7 @@ static int si2157_set_params(struct dvb_frontend *fe)
if (ret)
goto err;
- /* set if frequency if needed */
+ /* set digital if frequency if needed */
if (if_frequency != dev->if_frequency) {
memcpy(cmd.args, "\x14\x00\x06\x07", 4);
cmd.args[4] = (if_frequency / 1000) & 0xff;
@@ -402,7 +402,7 @@ static int si2157_set_params(struct dvb_frontend *fe)
dev->if_frequency = if_frequency;
}
- /* set frequency */
+ /* set digital frequency */
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;
@@ -414,18 +414,254 @@ static int si2157_set_params(struct dvb_frontend *fe)
if (ret)
goto err;
+ dev->bandwidth = bandwidth;
+ dev->frequency = c->frequency;
+
+ return 0;
+err:
+ dev->bandwidth = 0;
+ dev->frequency = 0;
+ dev->if_frequency = 0;
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int si2157_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct i2c_client *client = fe->tuner_priv;
+ struct si2157_dev *dev = i2c_get_clientdata(client);
+ char *std; /* for debugging */
+ int ret;
+ struct si2157_cmd cmd;
+ u32 bandwidth = 0;
+ u32 if_frequency = 0;
+ u32 freq = 0;
+ u64 tmp_lval = 0;
+ u8 system = 0;
+ u8 color = 0; /* 0=NTSC/PAL, 0x10=SECAM */
+ u8 invert_analog = 1; /* analog tuner spectrum; 0=normal, 1=inverted */
+
+ if (dev->chiptype != SI2157_CHIPTYPE_SI2157) {
+ dev_info(&client->dev, "%s: Analog tuning not supported for chiptype=%u\n",
+ __func__, dev->chiptype);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (!dev->active)
+ si2157_init(fe);
+
+ if (!dev->active) {
+ ret = -EAGAIN;
+ goto err;
+ }
+ if (params->mode == V4L2_TUNER_RADIO) {
+ /*
+ * std = "fm";
+ * bandwidth = 1700000; //best can do for FM, AGC will be a mess though
+ * if_frequency = 1250000; //HVR-225x(saa7164), HVR-12xx(cx23885)
+ * if_frequency = 6600000; //HVR-9xx(cx231xx)
+ * if_frequency = 5500000; //HVR-19xx(pvrusb2)
+ */
+ dev_err(&client->dev, "si2157 does not currently support FM radio\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ tmp_lval = params->frequency * 625LL;
+ do_div(tmp_lval, 10); /* convert to HZ */
+ freq = (u32)tmp_lval;
+
+ if (freq < 1000000) /* is freq in KHz */
+ freq = freq * 1000;
+ dev->frequency = freq;
+
+ /* if_frequency values based on tda187271C2 */
+ if (params->std & (V4L2_STD_B|V4L2_STD_GH)) {
+ if (freq >= 470000000) {
+ std = "palGH";
+ bandwidth = 8000000;
+ if_frequency = 6000000;
+ system = 1;
+ if (params->std & (V4L2_STD_SECAM_G|V4L2_STD_SECAM_H)) {
+ std = "secamGH";
+ color = 0x10;
+ }
+ } else {
+ std = "palB";
+ bandwidth = 7000000;
+ if_frequency = 6000000;
+ system = 0;
+ if (params->std & V4L2_STD_SECAM_B) {
+ std = "secamB";
+ color = 0x10;
+ }
+ }
+ } else if (params->std & V4L2_STD_MN) {
+ std = "MN";
+ bandwidth = 6000000;
+ if_frequency = 5400000;
+ system = 2;
+ } else if (params->std & V4L2_STD_PAL_I) {
+ std = "palI";
+ bandwidth = 8000000;
+ if_frequency = 7250000; /* TODO: does not work yet */
+ system = 4;
+ } else if (params->std & V4L2_STD_DK) {
+ std = "palDK";
+ bandwidth = 8000000;
+ if_frequency = 6900000; /* TODO: does not work yet */
+ system = 5;
+ if (params->std & V4L2_STD_SECAM_DK) {
+ std = "secamDK";
+ color = 0x10;
+ }
+ } else if (params->std & V4L2_STD_SECAM_L) {
+ std = "secamL";
+ bandwidth = 8000000;
+ if_frequency = 6750000; /* TODO: untested */
+ system = 6;
+ color = 0x10;
+ } else if (params->std & V4L2_STD_SECAM_LC) {
+ std = "secamL'";
+ bandwidth = 7000000;
+ if_frequency = 1250000; /* TODO: untested */
+ system = 7;
+ color = 0x10;
+ } else {
+ std = "unknown";
+ }
+ /* calc channel center freq */
+ freq = freq - 1250000 + (bandwidth/2);
+
+ dev_dbg(&client->dev,
+ "mode=%d system=%u std='%s' params->frequency=%u center freq=%u if=%u bandwidth=%u\n",
+ params->mode, system, std, params->frequency,
+ freq, if_frequency, bandwidth);
+
+ /* set analog IF port */
+ memcpy(cmd.args, "\x14\x00\x03\x06\x08\x02", 6);
+ /* in using dev->if_port, we assume analog and digital IF's */
+ /* are always on different ports */
+ /* assumes if_port definition is 0 or 1 for digital out */
+ cmd.args[4] = (dev->if_port == 1)?8:10;
+ cmd.args[5] = (dev->if_port == 1)?2:1; /* Analog AGC assumed external */
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* set analog IF output config */
+ memcpy(cmd.args, "\x14\x00\x0d\x06\x94\x64", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* make this distinct from a digital IF */
+ dev->if_frequency = if_frequency | 1;
+
+ /* calc and set tuner analog if center frequency */
+ if_frequency = if_frequency + 1250000 - (bandwidth/2);
+ dev_dbg(&client->dev, "IF Ctr freq=%d\n", if_frequency);
+
+ memcpy(cmd.args, "\x14\x00\x0C\x06", 4);
+ cmd.args[4] = (if_frequency / 1000) & 0xff;
+ cmd.args[5] = ((if_frequency / 1000) >> 8) & 0xff;
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* set analog AGC config */
+ memcpy(cmd.args, "\x14\x00\x07\x06\x32\xc8", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 4;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* set analog video mode */
+ memcpy(cmd.args, "\x14\x00\x04\x06\x00\x00", 6);
+ cmd.args[4] = system | color;
+#if 1 /* can use dev->inversion if assumed it applies to both digital/analog */
+ if (invert_analog)
+ cmd.args[5] |= 0x02;
+#else
+ if (dev->inversion)
+ cmd.args[5] |= 0x02;
+#endif
+ cmd.wlen = 6;
+ cmd.rlen = 1;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ /* set analog frequency */
+ memcpy(cmd.args, "\x41\x01\x00\x00\x00\x00\x00\x00", 8);
+ cmd.args[4] = (freq >> 0) & 0xff;
+ cmd.args[5] = (freq >> 8) & 0xff;
+ cmd.args[6] = (freq >> 16) & 0xff;
+ cmd.args[7] = (freq >> 24) & 0xff;
+ cmd.wlen = 8;
+ cmd.rlen = 1;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+#if 0 /* testing */
+ /* get tuner status, RSSI values */
+ memcpy(cmd.args, "\x42\x01", 2);
+ cmd.wlen = 2;
+ cmd.rlen = 12;
+ ret = si2157_cmd_execute(client, &cmd);
+
+ dev_info(&client->dev, "%s: tuner status: ret=%d rssi=%d mode=%x freq=%d\n",
+ __func__, ret, cmd.args[3], cmd.args[8],
+ (cmd.args[7]<<24 | cmd.args[6]<<16 |
+ cmd.args[5]<<8 | cmd.args[4]));
+#endif
+ dev->bandwidth = bandwidth;
+
return 0;
err:
+ dev->bandwidth = 0;
+ dev->frequency = 0;
+ dev->if_frequency = 0;
dev_dbg(&client->dev, "failed=%d\n", ret);
return ret;
}
+static int si2157_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct i2c_client *client = fe->tuner_priv;
+ struct si2157_dev *dev = i2c_get_clientdata(client);
+
+ *frequency = dev->frequency;
+ dev_dbg(&client->dev, "%s: freq=%u\n", __func__, dev->frequency);
+ return 0;
+}
+
+static int si2157_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct i2c_client *client = fe->tuner_priv;
+ struct si2157_dev *dev = i2c_get_clientdata(client);
+
+ *bandwidth = dev->bandwidth;
+ dev_dbg(&client->dev, "%s: bandwidth=%u\n", __func__, dev->bandwidth);
+ return 0;
+}
+
static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct i2c_client *client = fe->tuner_priv;
struct si2157_dev *dev = i2c_get_clientdata(client);
- *frequency = dev->if_frequency;
+ *frequency = dev->if_frequency & ~1; /* strip analog IF indicator bit */
+ dev_dbg(&client->dev, "%s: if_frequency=%u\n", __func__, *frequency);
return 0;
}
@@ -439,6 +675,9 @@ static const struct dvb_tuner_ops si2157_ops = {
.init = si2157_init,
.sleep = si2157_sleep,
.set_params = si2157_set_params,
+ .set_analog_params = si2157_set_analog_params,
+ .get_frequency = si2157_get_frequency,
+ .get_bandwidth = si2157_get_bandwidth,
.get_if_frequency = si2157_get_if_frequency,
};
@@ -37,6 +37,8 @@ struct si2157_dev {
u8 chiptype;
u8 if_port;
u32 if_frequency;
+ u32 bandwidth;
+ u32 frequency;
struct delayed_work stat_work;
#if defined(CONFIG_MEDIA_CONTROLLER)
Include set_analog_params, get_frequency, and get_bandwidth. Tested with NTSC and PAL standards via ch3/4 generator. Other standards are included, but are untested due to lack of generator. Signed-off-by: Brad Love <brad@nextdimension.cc> --- drivers/media/tuners/si2157.c | 245 ++++++++++++++++++++++++++++++++++++- drivers/media/tuners/si2157_priv.h | 2 + 2 files changed, 244 insertions(+), 3 deletions(-)