From 1543e317f1da31b75942316931e8f491a8920811 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Thu, 04 Jan 2024 10:08:02 +0000
Subject: [PATCH] disable FB
---
kernel/drivers/media/tuners/si2157.c | 438 +++++++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 415 insertions(+), 23 deletions(-)
diff --git a/kernel/drivers/media/tuners/si2157.c b/kernel/drivers/media/tuners/si2157.c
index a4bba66..75ddf7e 100644
--- a/kernel/drivers/media/tuners/si2157.c
+++ b/kernel/drivers/media/tuners/si2157.c
@@ -1,22 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver
*
* Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
- *
- * 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 "si2157_priv.h"
static const struct dvb_tuner_ops si2157_ops;
+
+static int tuner_lock_debug;
+module_param(tuner_lock_debug, int, 0644);
+MODULE_PARM_DESC(tuner_lock_debug, "if set, signal lock is briefly waited on after setting params");
/* execute firmware command */
static int si2157_cmd_execute(struct i2c_client *client, struct si2157_cmd *cmd)
@@ -56,12 +51,18 @@
break;
}
- dev_dbg(&client->dev, "cmd execution took %d ms\n",
- jiffies_to_msecs(jiffies) -
- (jiffies_to_msecs(timeout) - TIMEOUT));
+ dev_dbg(&client->dev, "cmd execution took %d ms, status=%x\n",
+ jiffies_to_msecs(jiffies) -
+ (jiffies_to_msecs(timeout) - TIMEOUT),
+ cmd->args[0]);
if (!((cmd->args[0] >> 7) & 0x01)) {
ret = -ETIMEDOUT;
+ goto err_mutex_unlock;
+ }
+ /* check error status bit */
+ if (cmd->args[0] & 0x40) {
+ ret = -EAGAIN;
goto err_mutex_unlock;
}
}
@@ -114,7 +115,7 @@
}
cmd.rlen = 1;
ret = si2157_cmd_execute(client, &cmd);
- if (ret)
+ if (ret && (dev->chiptype != SI2157_CHIPTYPE_SI2141 || ret != -EAGAIN))
goto err;
/* Si2141 needs a second command before it answers the revision query */
@@ -124,6 +125,11 @@
ret = si2157_cmd_execute(client, &cmd);
if (ret)
goto err;
+ }
+
+ if (dev->dont_load_firmware) {
+ dev_info(&client->dev, "device is buggy, skipping firmware download\n");
+ goto skip_fw_download;
}
/* query chip revision */
@@ -137,6 +143,7 @@
chip_id = cmd.args[1] << 24 | cmd.args[2] << 16 | cmd.args[3] << 8 |
cmd.args[4] << 0;
+ #define SI2177_A30 ('A' << 24 | 77 << 16 | '3' << 8 | '0' << 0)
#define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0)
#define SI2148_A20 ('A' << 24 | 48 << 16 | '2' << 8 | '0' << 0)
#define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0)
@@ -151,6 +158,9 @@
break;
case SI2141_A10:
fw_name = SI2141_A10_FIRMWARE;
+ break;
+ case SI2177_A30:
+ fw_name = SI2157_A30_FIRMWARE;
break;
case SI2157_A30:
case SI2147_A30:
@@ -229,6 +239,28 @@
dev_info(&client->dev, "firmware version: %c.%c.%d\n",
cmd.args[6], cmd.args[7], cmd.args[8]);
+
+ /* enable tuner status flags */
+ memcpy(cmd.args, "\x14\x00\x01\x05\x01\x00", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 1;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ memcpy(cmd.args, "\x14\x00\x01\x06\x01\x00", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 1;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ memcpy(cmd.args, "\x14\x00\x01\x07\x01\x00", 6);
+ cmd.wlen = 6;
+ cmd.rlen = 1;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
warm:
/* init statistics in order signal app which are supported */
c->strength.len = 1;
@@ -270,6 +302,93 @@
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int si2157_tune_wait(struct i2c_client *client, u8 is_digital)
+{
+#define TUN_TIMEOUT 40
+#define DIG_TIMEOUT 30
+#define ANALOG_TIMEOUT 150
+ struct si2157_dev *dev = i2c_get_clientdata(client);
+ int ret;
+ unsigned long timeout;
+ unsigned long start_time;
+ u8 wait_status;
+ u8 tune_lock_mask;
+
+ if (is_digital)
+ tune_lock_mask = 0x04;
+ else
+ tune_lock_mask = 0x02;
+
+ mutex_lock(&dev->i2c_mutex);
+
+ /* wait tuner command complete */
+ start_time = jiffies;
+ timeout = start_time + msecs_to_jiffies(TUN_TIMEOUT);
+ while (1) {
+ ret = i2c_master_recv(client, &wait_status,
+ sizeof(wait_status));
+ if (ret < 0) {
+ goto err_mutex_unlock;
+ } else if (ret != sizeof(wait_status)) {
+ ret = -EREMOTEIO;
+ goto err_mutex_unlock;
+ }
+
+ if (time_after(jiffies, timeout))
+ break;
+
+ /* tuner done? */
+ if ((wait_status & 0x81) == 0x81)
+ break;
+ usleep_range(5000, 10000);
+ }
+
+ dev_dbg(&client->dev, "tuning took %d ms, status=0x%x\n",
+ jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_time),
+ wait_status);
+
+ /* if we tuned ok, wait a bit for tuner lock */
+ if (tuner_lock_debug && (wait_status & 0x81) == 0x81) {
+ if (is_digital)
+ timeout = jiffies + msecs_to_jiffies(DIG_TIMEOUT);
+ else
+ timeout = jiffies + msecs_to_jiffies(ANALOG_TIMEOUT);
+
+ while (!time_after(jiffies, timeout)) {
+ ret = i2c_master_recv(client, &wait_status,
+ sizeof(wait_status));
+ if (ret < 0) {
+ goto err_mutex_unlock;
+ } else if (ret != sizeof(wait_status)) {
+ ret = -EREMOTEIO;
+ goto err_mutex_unlock;
+ }
+
+ /* tuner locked? */
+ if (wait_status & tune_lock_mask)
+ break;
+ usleep_range(5000, 10000);
+ }
+
+ dev_dbg(&client->dev, "tuning+lock took %d ms, status=0x%x\n",
+ jiffies_to_msecs(jiffies) - jiffies_to_msecs(start_time),
+ wait_status);
+ }
+
+ if ((wait_status & 0xc0) != 0x80) {
+ ret = -ETIMEDOUT;
+ goto err_mutex_unlock;
+ }
+
+ mutex_unlock(&dev->i2c_mutex);
+ return 0;
+
+err_mutex_unlock:
+ mutex_unlock(&dev->i2c_mutex);
+ dev_err(&client->dev, "failed=%d\n", ret);
return ret;
}
@@ -343,7 +462,7 @@
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;
@@ -357,7 +476,7 @@
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;
@@ -369,10 +488,235 @@
if (ret)
goto err;
+ dev->bandwidth = bandwidth;
+ dev->frequency = c->frequency;
+
+ si2157_tune_wait(client, 1); /* wait to complete, ignore any errors */
+
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, "Analog tuning not supported for chiptype=%u\n",
+ 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;
+ /* Analog AGC assumed external */
+ cmd.args[5] = (dev->if_port == 1) ? 2 : 1;
+ 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;
+ /* can use dev->inversion if assumed applies to both digital/analog */
+ if (invert_analog)
+ cmd.args[5] |= 0x02;
+ 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;
+
+ dev->bandwidth = bandwidth;
+
+ si2157_tune_wait(client, 0); /* wait to complete, ignore any errors */
+
+ 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, "freq=%u\n", 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, "bandwidth=%u\n", dev->bandwidth);
+ return 0;
}
static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
@@ -380,8 +724,45 @@
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, "if_frequency=%u\n", *frequency);
return 0;
+}
+
+static int si2157_get_rf_strength(struct dvb_frontend *fe, u16 *rssi)
+{
+ struct i2c_client *client = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct si2157_cmd cmd;
+ int ret;
+ int strength;
+
+ dev_dbg(&client->dev, "\n");
+
+ memcpy(cmd.args, "\x42\x00", 2);
+ cmd.wlen = 2;
+ cmd.rlen = 12;
+ ret = si2157_cmd_execute(client, &cmd);
+ if (ret)
+ goto err;
+
+ c->strength.stat[0].scale = FE_SCALE_DECIBEL;
+ c->strength.stat[0].svalue = (s8)cmd.args[3] * 1000;
+
+ /* normalize values based on Silicon Labs reference
+ * add 100, then anything > 80 is 100% signal
+ */
+ strength = (s8)cmd.args[3] + 100;
+ strength = clamp_val(strength, 0, 80);
+ *rssi = (u16)(strength * 0xffff / 80);
+
+ dev_dbg(&client->dev, "strength=%d rssi=%u\n",
+ (s8)cmd.args[3], *rssi);
+
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ return ret;
}
static const struct dvb_tuner_ops si2157_ops = {
@@ -394,7 +775,12 @@
.init = si2157_init,
.sleep = si2157_sleep,
.set_params = si2157_set_params,
- .get_if_frequency = si2157_get_if_frequency,
+ .set_analog_params = si2157_set_analog_params,
+ .get_frequency = si2157_get_frequency,
+ .get_bandwidth = si2157_get_bandwidth,
+ .get_if_frequency = si2157_get_if_frequency,
+
+ .get_rf_strength = si2157_get_rf_strength,
};
static void si2157_stat_work(struct work_struct *work)
@@ -444,6 +830,7 @@
i2c_set_clientdata(client, dev);
dev->fe = cfg->fe;
dev->inversion = cfg->inversion;
+ dev->dont_load_firmware = cfg->dont_load_firmware;
dev->if_port = cfg->if_port;
dev->chiptype = (u8)id->driver_data;
dev->if_frequency = 5000000; /* default value of property 0x0706 */
@@ -454,7 +841,7 @@
cmd.wlen = 0;
cmd.rlen = 1;
ret = si2157_cmd_execute(client, &cmd);
- if (ret)
+ if (ret && ret != -EAGAIN)
goto err_kfree;
memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops));
@@ -467,11 +854,14 @@
dev->ent.name = KBUILD_MODNAME;
dev->ent.function = MEDIA_ENT_F_TUNER;
- dev->pad[TUNER_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
- dev->pad[TUNER_PAD_OUTPUT].flags = MEDIA_PAD_FL_SOURCE;
- dev->pad[TUNER_PAD_AUD_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ dev->pad[SI2157_PAD_RF_INPUT].flags = MEDIA_PAD_FL_SINK;
+ dev->pad[SI2157_PAD_RF_INPUT].sig_type = PAD_SIGNAL_ANALOG;
+ dev->pad[SI2157_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ dev->pad[SI2157_PAD_VID_OUT].sig_type = PAD_SIGNAL_ANALOG;
+ dev->pad[SI2157_PAD_AUD_OUT].flags = MEDIA_PAD_FL_SOURCE;
+ dev->pad[SI2157_PAD_AUD_OUT].sig_type = PAD_SIGNAL_AUDIO;
- ret = media_entity_pads_init(&dev->ent, TUNER_NUM_PADS,
+ ret = media_entity_pads_init(&dev->ent, SI2157_NUM_PADS,
&dev->pad[0]);
if (ret)
@@ -525,6 +915,7 @@
{"si2157", SI2157_CHIPTYPE_SI2157},
{"si2146", SI2157_CHIPTYPE_SI2146},
{"si2141", SI2157_CHIPTYPE_SI2141},
+ {"si2177", SI2157_CHIPTYPE_SI2177},
{}
};
MODULE_DEVICE_TABLE(i2c, si2157_id_table);
@@ -546,3 +937,4 @@
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(SI2158_A20_FIRMWARE);
MODULE_FIRMWARE(SI2141_A10_FIRMWARE);
+MODULE_FIRMWARE(SI2157_A30_FIRMWARE);
--
Gitblit v1.6.2