You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2528 lines
61 KiB
2528 lines
61 KiB
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
|
|
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
|
|
@@ -141,3 +141,10 @@ config DVB_USB_RTL28XXU
|
|
help
|
|
Say Y here to support the Realtek RTL28xxU DVB USB receiver.
|
|
|
|
+config DVB_USB_DVBSKY
|
|
+ tristate "DVBSky USB support"
|
|
+ depends on DVB_USB_V2
|
|
+ select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
|
|
+ select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
|
|
+ help
|
|
+ Say Y here to support the USB receivers from DVBSky.
|
|
--- a/drivers/media/usb/dvb-usb-v2/Makefile
|
|
+++ b/drivers/media/usb/dvb-usb-v2/Makefile
|
|
@@ -37,6 +37,9 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
|
|
dvb-usb-rtl28xxu-objs := rtl28xxu.o
|
|
obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
|
|
|
|
+dvb-usb-dvbsky-objs := dvbsky.o
|
|
+obj-$(CONFIG_DVB_USB_DVBSKY) += dvb-usb-dvbsky.o
|
|
+
|
|
ccflags-y += -I$(srctree)/drivers/media/dvb-core
|
|
ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
|
|
ccflags-y += -I$(srctree)/drivers/media/tuners
|
|
--- a/drivers/media/usb/dvb-usb-v2/dvbsky.c
|
|
+++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c
|
|
@@ -0,0 +1,864 @@
|
|
+/*
|
|
+ * Driver for DVBSky USB2.0 receiver
|
|
+ *
|
|
+ * Copyright (C) 2013 Max nibble <nibble.max@gmail.com>
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program; if not, write to the Free Software
|
|
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
+ */
|
|
+
|
|
+#include "dvb_usb.h"
|
|
+#include "m88ds3103.h"
|
|
+#include "ts2020.h"
|
|
+#include "sp2.h"
|
|
+#include "si2168.h"
|
|
+#include "si2157.h"
|
|
+
|
|
+#define DVBSKY_MSG_DELAY 0/*2000*/
|
|
+#define DVBSKY_BUF_LEN 64
|
|
+
|
|
+static int dvb_usb_dvbsky_disable_rc;
|
|
+module_param_named(disable_rc, dvb_usb_dvbsky_disable_rc, int, 0644);
|
|
+MODULE_PARM_DESC(disable_rc, "Disable inbuilt IR receiver.");
|
|
+
|
|
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
|
+
|
|
+struct dvbsky_state {
|
|
+ struct mutex stream_mutex;
|
|
+ u8 ibuf[DVBSKY_BUF_LEN];
|
|
+ u8 obuf[DVBSKY_BUF_LEN];
|
|
+ u8 last_lock;
|
|
+ struct i2c_client *i2c_client_demod;
|
|
+ struct i2c_client *i2c_client_tuner;
|
|
+ struct i2c_client *i2c_client_ci;
|
|
+
|
|
+ /* fe hook functions*/
|
|
+ int (*fe_set_voltage)(struct dvb_frontend *fe,
|
|
+ fe_sec_voltage_t voltage);
|
|
+ int (*fe_read_status)(struct dvb_frontend *fe,
|
|
+ fe_status_t *status);
|
|
+};
|
|
+
|
|
+static int dvbsky_usb_generic_rw(struct dvb_usb_device *d,
|
|
+ u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
|
|
+{
|
|
+ int ret;
|
|
+ struct dvbsky_state *state = d_to_priv(d);
|
|
+
|
|
+ mutex_lock(&d->usb_mutex);
|
|
+ if (wlen != 0)
|
|
+ memcpy(state->obuf, wbuf, wlen);
|
|
+
|
|
+ ret = dvb_usbv2_generic_rw_locked(d, state->obuf, wlen,
|
|
+ state->ibuf, rlen);
|
|
+
|
|
+ if (!ret && (rlen != 0))
|
|
+ memcpy(rbuf, state->ibuf, rlen);
|
|
+
|
|
+ mutex_unlock(&d->usb_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int dvbsky_stream_ctrl(struct dvb_usb_device *d, u8 onoff)
|
|
+{
|
|
+ struct dvbsky_state *state = d_to_priv(d);
|
|
+ int ret;
|
|
+ u8 obuf_pre[3] = { 0x37, 0, 0 };
|
|
+ u8 obuf_post[3] = { 0x36, 3, 0 };
|
|
+
|
|
+ mutex_lock(&state->stream_mutex);
|
|
+ ret = dvbsky_usb_generic_rw(d, obuf_pre, 3, NULL, 0);
|
|
+ if (!ret && onoff) {
|
|
+ msleep(20);
|
|
+ ret = dvbsky_usb_generic_rw(d, obuf_post, 3, NULL, 0);
|
|
+ }
|
|
+ mutex_unlock(&state->stream_mutex);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int dvbsky_streaming_ctrl(struct dvb_frontend *fe, int onoff)
|
|
+{
|
|
+ struct dvb_usb_device *d = fe_to_d(fe);
|
|
+
|
|
+ return dvbsky_stream_ctrl(d, (onoff == 0) ? 0 : 1);
|
|
+}
|
|
+
|
|
+/* GPIO */
|
|
+static int dvbsky_gpio_ctrl(struct dvb_usb_device *d, u8 gport, u8 value)
|
|
+{
|
|
+ int ret;
|
|
+ u8 obuf[3], ibuf[2];
|
|
+
|
|
+ obuf[0] = 0x0e;
|
|
+ obuf[1] = gport;
|
|
+ obuf[2] = value;
|
|
+ ret = dvbsky_usb_generic_rw(d, obuf, 3, ibuf, 1);
|
|
+ if (ret)
|
|
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* I2C */
|
|
+static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
|
|
+ int num)
|
|
+{
|
|
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
|
|
+ int ret = 0;
|
|
+ u8 ibuf[64], obuf[64];
|
|
+
|
|
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
|
|
+ return -EAGAIN;
|
|
+
|
|
+ if (num > 2) {
|
|
+ dev_err(&d->udev->dev,
|
|
+ "too many i2c messages[%d], max 2.", num);
|
|
+ ret = -EOPNOTSUPP;
|
|
+ goto i2c_error;
|
|
+ }
|
|
+
|
|
+ if (num == 1) {
|
|
+ if (msg[0].len > 60) {
|
|
+ dev_err(&d->udev->dev,
|
|
+ "too many i2c bytes[%d], max 60.",
|
|
+ msg[0].len);
|
|
+ ret = -EOPNOTSUPP;
|
|
+ goto i2c_error;
|
|
+ }
|
|
+ if (msg[0].flags & I2C_M_RD) {
|
|
+ /* single read */
|
|
+ obuf[0] = 0x09;
|
|
+ obuf[1] = 0;
|
|
+ obuf[2] = msg[0].len;
|
|
+ obuf[3] = msg[0].addr;
|
|
+ ret = dvbsky_usb_generic_rw(d, obuf, 4,
|
|
+ ibuf, msg[0].len + 1);
|
|
+ if (ret)
|
|
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
|
|
+ if (!ret)
|
|
+ memcpy(msg[0].buf, &ibuf[1], msg[0].len);
|
|
+ } else {
|
|
+ /* write */
|
|
+ obuf[0] = 0x08;
|
|
+ obuf[1] = msg[0].addr;
|
|
+ obuf[2] = msg[0].len;
|
|
+ memcpy(&obuf[3], msg[0].buf, msg[0].len);
|
|
+ ret = dvbsky_usb_generic_rw(d, obuf,
|
|
+ msg[0].len + 3, ibuf, 1);
|
|
+ if (ret)
|
|
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
|
|
+ }
|
|
+ } else {
|
|
+ if ((msg[0].len > 60) || (msg[1].len > 60)) {
|
|
+ dev_err(&d->udev->dev,
|
|
+ "too many i2c bytes[w-%d][r-%d], max 60.",
|
|
+ msg[0].len, msg[1].len);
|
|
+ ret = -EOPNOTSUPP;
|
|
+ goto i2c_error;
|
|
+ }
|
|
+ /* write then read */
|
|
+ obuf[0] = 0x09;
|
|
+ obuf[1] = msg[0].len;
|
|
+ obuf[2] = msg[1].len;
|
|
+ obuf[3] = msg[0].addr;
|
|
+ memcpy(&obuf[4], msg[0].buf, msg[0].len);
|
|
+ ret = dvbsky_usb_generic_rw(d, obuf,
|
|
+ msg[0].len + 4, ibuf, msg[1].len + 1);
|
|
+ if (ret)
|
|
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
|
|
+
|
|
+ if (!ret)
|
|
+ memcpy(msg[1].buf, &ibuf[1], msg[1].len);
|
|
+ }
|
|
+i2c_error:
|
|
+ mutex_unlock(&d->i2c_mutex);
|
|
+ return (ret) ? ret : num;
|
|
+}
|
|
+
|
|
+static u32 dvbsky_i2c_func(struct i2c_adapter *adapter)
|
|
+{
|
|
+ return I2C_FUNC_I2C;
|
|
+}
|
|
+
|
|
+static struct i2c_algorithm dvbsky_i2c_algo = {
|
|
+ .master_xfer = dvbsky_i2c_xfer,
|
|
+ .functionality = dvbsky_i2c_func,
|
|
+};
|
|
+
|
|
+#if IS_ENABLED(CONFIG_RC_CORE)
|
|
+static int dvbsky_rc_query(struct dvb_usb_device *d)
|
|
+{
|
|
+ u32 code = 0xffff, scancode;
|
|
+ u8 rc5_command, rc5_system;
|
|
+ u8 obuf[2], ibuf[2], toggle;
|
|
+ int ret;
|
|
+
|
|
+ obuf[0] = 0x10;
|
|
+ ret = dvbsky_usb_generic_rw(d, obuf, 1, ibuf, 2);
|
|
+ if (ret)
|
|
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
|
|
+ if (ret == 0)
|
|
+ code = (ibuf[0] << 8) | ibuf[1];
|
|
+ if (code != 0xffff) {
|
|
+ dev_dbg(&d->udev->dev, "rc code: %x\n", code);
|
|
+ rc5_command = code & 0x3F;
|
|
+ rc5_system = (code & 0x7C0) >> 6;
|
|
+ toggle = (code & 0x800) ? 1 : 0;
|
|
+ scancode = rc5_system << 8 | rc5_command;
|
|
+ rc_keydown(d->rc_dev, scancode, toggle);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
|
|
+{
|
|
+ if (dvb_usb_dvbsky_disable_rc) {
|
|
+ rc->map_name = NULL;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ rc->allowed_protos = RC_BIT_RC5;
|
|
+ rc->query = dvbsky_rc_query;
|
|
+ rc->interval = 300;
|
|
+ return 0;
|
|
+}
|
|
+#else
|
|
+ #define dvbsky_get_rc_config NULL
|
|
+#endif
|
|
+
|
|
+static int dvbsky_usb_set_voltage(struct dvb_frontend *fe,
|
|
+ fe_sec_voltage_t voltage)
|
|
+{
|
|
+ struct dvb_usb_device *d = fe_to_d(fe);
|
|
+ struct dvbsky_state *state = d_to_priv(d);
|
|
+ u8 value;
|
|
+
|
|
+ if (voltage == SEC_VOLTAGE_OFF)
|
|
+ value = 0;
|
|
+ else
|
|
+ value = 1;
|
|
+ dvbsky_gpio_ctrl(d, 0x80, value);
|
|
+
|
|
+ return state->fe_set_voltage(fe, voltage);
|
|
+}
|
|
+
|
|
+static int dvbsky_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6])
|
|
+{
|
|
+ struct dvb_usb_device *d = adap_to_d(adap);
|
|
+ u8 obuf[] = { 0x1e, 0x00 };
|
|
+ u8 ibuf[6] = { 0 };
|
|
+ struct i2c_msg msg[] = {
|
|
+ {
|
|
+ .addr = 0x51,
|
|
+ .flags = 0,
|
|
+ .buf = obuf,
|
|
+ .len = 2,
|
|
+ }, {
|
|
+ .addr = 0x51,
|
|
+ .flags = I2C_M_RD,
|
|
+ .buf = ibuf,
|
|
+ .len = 6,
|
|
+ }
|
|
+ };
|
|
+
|
|
+ if (i2c_transfer(&d->i2c_adap, msg, 2) == 2)
|
|
+ memcpy(mac, ibuf, 6);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_usb_read_status(struct dvb_frontend *fe, fe_status_t *status)
|
|
+{
|
|
+ struct dvb_usb_device *d = fe_to_d(fe);
|
|
+ struct dvbsky_state *state = d_to_priv(d);
|
|
+ int ret;
|
|
+
|
|
+ ret = state->fe_read_status(fe, status);
|
|
+
|
|
+ /* it need resync slave fifo when signal change from unlock to lock.*/
|
|
+ if ((*status & FE_HAS_LOCK) && (!state->last_lock))
|
|
+ dvbsky_stream_ctrl(d, 1);
|
|
+
|
|
+ state->last_lock = (*status & FE_HAS_LOCK) ? 1 : 0;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct m88ds3103_config dvbsky_s960_m88ds3103_config = {
|
|
+ .i2c_addr = 0x68,
|
|
+ .clock = 27000000,
|
|
+ .i2c_wr_max = 33,
|
|
+ .clock_out = 0,
|
|
+ .ts_mode = M88DS3103_TS_CI,
|
|
+ .ts_clk = 16000,
|
|
+ .ts_clk_pol = 0,
|
|
+ .agc = 0x99,
|
|
+ .lnb_hv_pol = 1,
|
|
+ .lnb_en_pol = 1,
|
|
+};
|
|
+
|
|
+static int dvbsky_s960_attach(struct dvb_usb_adapter *adap)
|
|
+{
|
|
+ struct dvbsky_state *state = adap_to_priv(adap);
|
|
+ struct dvb_usb_device *d = adap_to_d(adap);
|
|
+ int ret = 0;
|
|
+ /* demod I2C adapter */
|
|
+ struct i2c_adapter *i2c_adapter;
|
|
+ struct i2c_client *client;
|
|
+ struct i2c_board_info info;
|
|
+ struct ts2020_config ts2020_config = {};
|
|
+ memset(&info, 0, sizeof(struct i2c_board_info));
|
|
+
|
|
+ /* attach demod */
|
|
+ adap->fe[0] = dvb_attach(m88ds3103_attach,
|
|
+ &dvbsky_s960_m88ds3103_config,
|
|
+ &d->i2c_adap,
|
|
+ &i2c_adapter);
|
|
+ if (!adap->fe[0]) {
|
|
+ dev_err(&d->udev->dev, "dvbsky_s960_attach fail.\n");
|
|
+ ret = -ENODEV;
|
|
+ goto fail_attach;
|
|
+ }
|
|
+
|
|
+ /* attach tuner */
|
|
+ ts2020_config.fe = adap->fe[0];
|
|
+ strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
|
|
+ info.addr = 0x60;
|
|
+ info.platform_data = &ts2020_config;
|
|
+ request_module("ts2020");
|
|
+ client = i2c_new_device(i2c_adapter, &info);
|
|
+ if (client == NULL || client->dev.driver == NULL) {
|
|
+ dvb_frontend_detach(adap->fe[0]);
|
|
+ ret = -ENODEV;
|
|
+ goto fail_attach;
|
|
+ }
|
|
+
|
|
+ if (!try_module_get(client->dev.driver->owner)) {
|
|
+ i2c_unregister_device(client);
|
|
+ dvb_frontend_detach(adap->fe[0]);
|
|
+ ret = -ENODEV;
|
|
+ goto fail_attach;
|
|
+ }
|
|
+
|
|
+ /* delegate signal strength measurement to tuner */
|
|
+ adap->fe[0]->ops.read_signal_strength =
|
|
+ adap->fe[0]->ops.tuner_ops.get_rf_strength;
|
|
+
|
|
+ /* hook fe: need to resync the slave fifo when signal locks. */
|
|
+ state->fe_read_status = adap->fe[0]->ops.read_status;
|
|
+ adap->fe[0]->ops.read_status = dvbsky_usb_read_status;
|
|
+
|
|
+ /* hook fe: LNB off/on is control by Cypress usb chip. */
|
|
+ state->fe_set_voltage = adap->fe[0]->ops.set_voltage;
|
|
+ adap->fe[0]->ops.set_voltage = dvbsky_usb_set_voltage;
|
|
+
|
|
+ state->i2c_client_tuner = client;
|
|
+
|
|
+fail_attach:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int dvbsky_usb_ci_set_voltage(struct dvb_frontend *fe,
|
|
+ fe_sec_voltage_t voltage)
|
|
+{
|
|
+ struct dvb_usb_device *d = fe_to_d(fe);
|
|
+ struct dvbsky_state *state = d_to_priv(d);
|
|
+ u8 value;
|
|
+
|
|
+ if (voltage == SEC_VOLTAGE_OFF)
|
|
+ value = 0;
|
|
+ else
|
|
+ value = 1;
|
|
+ dvbsky_gpio_ctrl(d, 0x00, value);
|
|
+
|
|
+ return state->fe_set_voltage(fe, voltage);
|
|
+}
|
|
+
|
|
+static int dvbsky_ci_ctrl(void *priv, u8 read, int addr,
|
|
+ u8 data, int *mem)
|
|
+{
|
|
+ struct dvb_usb_device *d = priv;
|
|
+ int ret = 0;
|
|
+ u8 command[4], respond[2], command_size, respond_size;
|
|
+
|
|
+ command[1] = (u8)((addr >> 8) & 0xff); /*high part of address*/
|
|
+ command[2] = (u8)(addr & 0xff); /*low part of address*/
|
|
+ if (read) {
|
|
+ command[0] = 0x71;
|
|
+ command_size = 3;
|
|
+ respond_size = 2;
|
|
+ } else {
|
|
+ command[0] = 0x70;
|
|
+ command[3] = data;
|
|
+ command_size = 4;
|
|
+ respond_size = 1;
|
|
+ }
|
|
+ ret = dvbsky_usb_generic_rw(d, command, command_size,
|
|
+ respond, respond_size);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ if (read)
|
|
+ *mem = respond[1];
|
|
+ return ret;
|
|
+err:
|
|
+ dev_err(&d->udev->dev, "ci control failed=%d\n", ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct m88ds3103_config dvbsky_s960c_m88ds3103_config = {
|
|
+ .i2c_addr = 0x68,
|
|
+ .clock = 27000000,
|
|
+ .i2c_wr_max = 33,
|
|
+ .clock_out = 0,
|
|
+ .ts_mode = M88DS3103_TS_CI,
|
|
+ .ts_clk = 10000,
|
|
+ .ts_clk_pol = 1,
|
|
+ .agc = 0x99,
|
|
+ .lnb_hv_pol = 0,
|
|
+ .lnb_en_pol = 1,
|
|
+};
|
|
+
|
|
+static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap)
|
|
+{
|
|
+ struct dvbsky_state *state = adap_to_priv(adap);
|
|
+ struct dvb_usb_device *d = adap_to_d(adap);
|
|
+ int ret = 0;
|
|
+ /* demod I2C adapter */
|
|
+ struct i2c_adapter *i2c_adapter;
|
|
+ struct i2c_client *client_tuner, *client_ci;
|
|
+ struct i2c_board_info info;
|
|
+ struct sp2_config sp2_config;
|
|
+ struct ts2020_config ts2020_config = {};
|
|
+ memset(&info, 0, sizeof(struct i2c_board_info));
|
|
+
|
|
+ /* attach demod */
|
|
+ adap->fe[0] = dvb_attach(m88ds3103_attach,
|
|
+ &dvbsky_s960c_m88ds3103_config,
|
|
+ &d->i2c_adap,
|
|
+ &i2c_adapter);
|
|
+ if (!adap->fe[0]) {
|
|
+ dev_err(&d->udev->dev, "dvbsky_s960ci_attach fail.\n");
|
|
+ ret = -ENODEV;
|
|
+ goto fail_attach;
|
|
+ }
|
|
+
|
|
+ /* attach tuner */
|
|
+ ts2020_config.fe = adap->fe[0];
|
|
+ strlcpy(info.type, "ts2020", I2C_NAME_SIZE);
|
|
+ info.addr = 0x60;
|
|
+ info.platform_data = &ts2020_config;
|
|
+ request_module("ts2020");
|
|
+ client_tuner = i2c_new_device(i2c_adapter, &info);
|
|
+ if (client_tuner == NULL || client_tuner->dev.driver == NULL) {
|
|
+ ret = -ENODEV;
|
|
+ goto fail_tuner_device;
|
|
+ }
|
|
+
|
|
+ if (!try_module_get(client_tuner->dev.driver->owner)) {
|
|
+ ret = -ENODEV;
|
|
+ goto fail_tuner_module;
|
|
+ }
|
|
+
|
|
+ /* attach ci controller */
|
|
+ memset(&sp2_config, 0, sizeof(sp2_config));
|
|
+ sp2_config.dvb_adap = &adap->dvb_adap;
|
|
+ sp2_config.priv = d;
|
|
+ sp2_config.ci_control = dvbsky_ci_ctrl;
|
|
+ memset(&info, 0, sizeof(struct i2c_board_info));
|
|
+ strlcpy(info.type, "sp2", I2C_NAME_SIZE);
|
|
+ info.addr = 0x40;
|
|
+ info.platform_data = &sp2_config;
|
|
+ request_module("sp2");
|
|
+ client_ci = i2c_new_device(&d->i2c_adap, &info);
|
|
+ if (client_ci == NULL || client_ci->dev.driver == NULL) {
|
|
+ ret = -ENODEV;
|
|
+ goto fail_ci_device;
|
|
+ }
|
|
+
|
|
+ if (!try_module_get(client_ci->dev.driver->owner)) {
|
|
+ ret = -ENODEV;
|
|
+ goto fail_ci_module;
|
|
+ }
|
|
+
|
|
+ /* delegate signal strength measurement to tuner */
|
|
+ adap->fe[0]->ops.read_signal_strength =
|
|
+ adap->fe[0]->ops.tuner_ops.get_rf_strength;
|
|
+
|
|
+ /* hook fe: need to resync the slave fifo when signal locks. */
|
|
+ state->fe_read_status = adap->fe[0]->ops.read_status;
|
|
+ adap->fe[0]->ops.read_status = dvbsky_usb_read_status;
|
|
+
|
|
+ /* hook fe: LNB off/on is control by Cypress usb chip. */
|
|
+ state->fe_set_voltage = adap->fe[0]->ops.set_voltage;
|
|
+ adap->fe[0]->ops.set_voltage = dvbsky_usb_ci_set_voltage;
|
|
+
|
|
+ state->i2c_client_tuner = client_tuner;
|
|
+ state->i2c_client_ci = client_ci;
|
|
+ return ret;
|
|
+fail_ci_module:
|
|
+ i2c_unregister_device(client_ci);
|
|
+fail_ci_device:
|
|
+ module_put(client_tuner->dev.driver->owner);
|
|
+fail_tuner_module:
|
|
+ i2c_unregister_device(client_tuner);
|
|
+fail_tuner_device:
|
|
+ dvb_frontend_detach(adap->fe[0]);
|
|
+fail_attach:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int dvbsky_t680c_attach(struct dvb_usb_adapter *adap)
|
|
+{
|
|
+ struct dvbsky_state *state = adap_to_priv(adap);
|
|
+ struct dvb_usb_device *d = adap_to_d(adap);
|
|
+ int ret = 0;
|
|
+ struct i2c_adapter *i2c_adapter;
|
|
+ struct i2c_client *client_demod, *client_tuner, *client_ci;
|
|
+ struct i2c_board_info info;
|
|
+ struct si2168_config si2168_config;
|
|
+ struct si2157_config si2157_config;
|
|
+ struct sp2_config sp2_config;
|
|
+
|
|
+ /* attach demod */
|
|
+ memset(&si2168_config, 0, sizeof(si2168_config));
|
|
+ si2168_config.i2c_adapter = &i2c_adapter;
|
|
+ si2168_config.fe = &adap->fe[0];
|
|
+ si2168_config.ts_mode = SI2168_TS_PARALLEL;
|
|
+ memset(&info, 0, sizeof(struct i2c_board_info));
|
|
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
|
|
+ info.addr = 0x64;
|
|
+ info.platform_data = &si2168_config;
|
|
+
|
|
+ request_module(info.type);
|
|
+ client_demod = i2c_new_device(&d->i2c_adap, &info);
|
|
+ if (client_demod == NULL ||
|
|
+ client_demod->dev.driver == NULL)
|
|
+ goto fail_demod_device;
|
|
+ if (!try_module_get(client_demod->dev.driver->owner))
|
|
+ goto fail_demod_module;
|
|
+
|
|
+ /* attach tuner */
|
|
+ memset(&si2157_config, 0, sizeof(si2157_config));
|
|
+ si2157_config.fe = adap->fe[0];
|
|
+ memset(&info, 0, sizeof(struct i2c_board_info));
|
|
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
|
|
+ info.addr = 0x60;
|
|
+ info.platform_data = &si2157_config;
|
|
+
|
|
+ request_module(info.type);
|
|
+ client_tuner = i2c_new_device(i2c_adapter, &info);
|
|
+ if (client_tuner == NULL ||
|
|
+ client_tuner->dev.driver == NULL)
|
|
+ goto fail_tuner_device;
|
|
+ if (!try_module_get(client_tuner->dev.driver->owner))
|
|
+ goto fail_tuner_module;
|
|
+
|
|
+ /* attach ci controller */
|
|
+ memset(&sp2_config, 0, sizeof(sp2_config));
|
|
+ sp2_config.dvb_adap = &adap->dvb_adap;
|
|
+ sp2_config.priv = d;
|
|
+ sp2_config.ci_control = dvbsky_ci_ctrl;
|
|
+ memset(&info, 0, sizeof(struct i2c_board_info));
|
|
+ strlcpy(info.type, "sp2", I2C_NAME_SIZE);
|
|
+ info.addr = 0x40;
|
|
+ info.platform_data = &sp2_config;
|
|
+
|
|
+ request_module(info.type);
|
|
+ client_ci = i2c_new_device(&d->i2c_adap, &info);
|
|
+
|
|
+ if (client_ci == NULL || client_ci->dev.driver == NULL)
|
|
+ goto fail_ci_device;
|
|
+
|
|
+ if (!try_module_get(client_ci->dev.driver->owner))
|
|
+ goto fail_ci_module;
|
|
+
|
|
+ state->i2c_client_demod = client_demod;
|
|
+ state->i2c_client_tuner = client_tuner;
|
|
+ state->i2c_client_ci = client_ci;
|
|
+ return ret;
|
|
+fail_ci_module:
|
|
+ i2c_unregister_device(client_ci);
|
|
+fail_ci_device:
|
|
+ module_put(client_tuner->dev.driver->owner);
|
|
+fail_tuner_module:
|
|
+ i2c_unregister_device(client_tuner);
|
|
+fail_tuner_device:
|
|
+ module_put(client_demod->dev.driver->owner);
|
|
+fail_demod_module:
|
|
+ i2c_unregister_device(client_demod);
|
|
+fail_demod_device:
|
|
+ ret = -ENODEV;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int dvbsky_t330_attach(struct dvb_usb_adapter *adap)
|
|
+{
|
|
+ struct dvbsky_state *state = adap_to_priv(adap);
|
|
+ struct dvb_usb_device *d = adap_to_d(adap);
|
|
+ int ret = 0;
|
|
+ struct i2c_adapter *i2c_adapter;
|
|
+ struct i2c_client *client_demod, *client_tuner;
|
|
+ struct i2c_board_info info;
|
|
+ struct si2168_config si2168_config;
|
|
+ struct si2157_config si2157_config;
|
|
+
|
|
+ /* attach demod */
|
|
+ memset(&si2168_config, 0, sizeof(si2168_config));
|
|
+ si2168_config.i2c_adapter = &i2c_adapter;
|
|
+ si2168_config.fe = &adap->fe[0];
|
|
+ si2168_config.ts_mode = SI2168_TS_PARALLEL | 0x40;
|
|
+ memset(&info, 0, sizeof(struct i2c_board_info));
|
|
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
|
|
+ info.addr = 0x64;
|
|
+ info.platform_data = &si2168_config;
|
|
+
|
|
+ request_module(info.type);
|
|
+ client_demod = i2c_new_device(&d->i2c_adap, &info);
|
|
+ if (client_demod == NULL ||
|
|
+ client_demod->dev.driver == NULL)
|
|
+ goto fail_demod_device;
|
|
+ if (!try_module_get(client_demod->dev.driver->owner))
|
|
+ goto fail_demod_module;
|
|
+
|
|
+ /* attach tuner */
|
|
+ memset(&si2157_config, 0, sizeof(si2157_config));
|
|
+ si2157_config.fe = adap->fe[0];
|
|
+ memset(&info, 0, sizeof(struct i2c_board_info));
|
|
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
|
|
+ info.addr = 0x60;
|
|
+ info.platform_data = &si2157_config;
|
|
+
|
|
+ request_module(info.type);
|
|
+ client_tuner = i2c_new_device(i2c_adapter, &info);
|
|
+ if (client_tuner == NULL ||
|
|
+ client_tuner->dev.driver == NULL)
|
|
+ goto fail_tuner_device;
|
|
+ if (!try_module_get(client_tuner->dev.driver->owner))
|
|
+ goto fail_tuner_module;
|
|
+
|
|
+ state->i2c_client_demod = client_demod;
|
|
+ state->i2c_client_tuner = client_tuner;
|
|
+ return ret;
|
|
+fail_tuner_module:
|
|
+ i2c_unregister_device(client_tuner);
|
|
+fail_tuner_device:
|
|
+ module_put(client_demod->dev.driver->owner);
|
|
+fail_demod_module:
|
|
+ i2c_unregister_device(client_demod);
|
|
+fail_demod_device:
|
|
+ ret = -ENODEV;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name)
|
|
+{
|
|
+ dvbsky_gpio_ctrl(d, 0x04, 1);
|
|
+ msleep(20);
|
|
+ dvbsky_gpio_ctrl(d, 0x83, 0);
|
|
+ dvbsky_gpio_ctrl(d, 0xc0, 1);
|
|
+ msleep(100);
|
|
+ dvbsky_gpio_ctrl(d, 0x83, 1);
|
|
+ dvbsky_gpio_ctrl(d, 0xc0, 0);
|
|
+ msleep(50);
|
|
+
|
|
+ return WARM;
|
|
+}
|
|
+
|
|
+static int dvbsky_init(struct dvb_usb_device *d)
|
|
+{
|
|
+ struct dvbsky_state *state = d_to_priv(d);
|
|
+
|
|
+ /* use default interface */
|
|
+ /*
|
|
+ ret = usb_set_interface(d->udev, 0, 0);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ */
|
|
+ mutex_init(&state->stream_mutex);
|
|
+
|
|
+ state->last_lock = 0;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void dvbsky_exit(struct dvb_usb_device *d)
|
|
+{
|
|
+ struct dvbsky_state *state = d_to_priv(d);
|
|
+ struct i2c_client *client;
|
|
+
|
|
+ client = state->i2c_client_tuner;
|
|
+ /* remove I2C tuner */
|
|
+ if (client) {
|
|
+ module_put(client->dev.driver->owner);
|
|
+ i2c_unregister_device(client);
|
|
+ }
|
|
+ client = state->i2c_client_demod;
|
|
+ /* remove I2C demod */
|
|
+ if (client) {
|
|
+ module_put(client->dev.driver->owner);
|
|
+ i2c_unregister_device(client);
|
|
+ }
|
|
+ client = state->i2c_client_ci;
|
|
+ /* remove I2C ci */
|
|
+ if (client) {
|
|
+ module_put(client->dev.driver->owner);
|
|
+ i2c_unregister_device(client);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* DVB USB Driver stuff */
|
|
+static struct dvb_usb_device_properties dvbsky_s960_props = {
|
|
+ .driver_name = KBUILD_MODNAME,
|
|
+ .owner = THIS_MODULE,
|
|
+ .adapter_nr = adapter_nr,
|
|
+ .size_of_priv = sizeof(struct dvbsky_state),
|
|
+
|
|
+ .generic_bulk_ctrl_endpoint = 0x01,
|
|
+ .generic_bulk_ctrl_endpoint_response = 0x81,
|
|
+ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
|
|
+
|
|
+ .i2c_algo = &dvbsky_i2c_algo,
|
|
+ .frontend_attach = dvbsky_s960_attach,
|
|
+ .init = dvbsky_init,
|
|
+ .get_rc_config = dvbsky_get_rc_config,
|
|
+ .streaming_ctrl = dvbsky_streaming_ctrl,
|
|
+ .identify_state = dvbsky_identify_state,
|
|
+ .exit = dvbsky_exit,
|
|
+ .read_mac_address = dvbsky_read_mac_addr,
|
|
+
|
|
+ .num_adapters = 1,
|
|
+ .adapter = {
|
|
+ {
|
|
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
|
|
+ }
|
|
+ }
|
|
+};
|
|
+
|
|
+static struct dvb_usb_device_properties dvbsky_s960c_props = {
|
|
+ .driver_name = KBUILD_MODNAME,
|
|
+ .owner = THIS_MODULE,
|
|
+ .adapter_nr = adapter_nr,
|
|
+ .size_of_priv = sizeof(struct dvbsky_state),
|
|
+
|
|
+ .generic_bulk_ctrl_endpoint = 0x01,
|
|
+ .generic_bulk_ctrl_endpoint_response = 0x81,
|
|
+ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
|
|
+
|
|
+ .i2c_algo = &dvbsky_i2c_algo,
|
|
+ .frontend_attach = dvbsky_s960c_attach,
|
|
+ .init = dvbsky_init,
|
|
+ .get_rc_config = dvbsky_get_rc_config,
|
|
+ .streaming_ctrl = dvbsky_streaming_ctrl,
|
|
+ .identify_state = dvbsky_identify_state,
|
|
+ .exit = dvbsky_exit,
|
|
+ .read_mac_address = dvbsky_read_mac_addr,
|
|
+
|
|
+ .num_adapters = 1,
|
|
+ .adapter = {
|
|
+ {
|
|
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
|
|
+ }
|
|
+ }
|
|
+};
|
|
+
|
|
+static struct dvb_usb_device_properties dvbsky_t680c_props = {
|
|
+ .driver_name = KBUILD_MODNAME,
|
|
+ .owner = THIS_MODULE,
|
|
+ .adapter_nr = adapter_nr,
|
|
+ .size_of_priv = sizeof(struct dvbsky_state),
|
|
+
|
|
+ .generic_bulk_ctrl_endpoint = 0x01,
|
|
+ .generic_bulk_ctrl_endpoint_response = 0x81,
|
|
+ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
|
|
+
|
|
+ .i2c_algo = &dvbsky_i2c_algo,
|
|
+ .frontend_attach = dvbsky_t680c_attach,
|
|
+ .init = dvbsky_init,
|
|
+ .get_rc_config = dvbsky_get_rc_config,
|
|
+ .streaming_ctrl = dvbsky_streaming_ctrl,
|
|
+ .identify_state = dvbsky_identify_state,
|
|
+ .exit = dvbsky_exit,
|
|
+ .read_mac_address = dvbsky_read_mac_addr,
|
|
+
|
|
+ .num_adapters = 1,
|
|
+ .adapter = {
|
|
+ {
|
|
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
|
|
+ }
|
|
+ }
|
|
+};
|
|
+
|
|
+static struct dvb_usb_device_properties dvbsky_t330_props = {
|
|
+ .driver_name = KBUILD_MODNAME,
|
|
+ .owner = THIS_MODULE,
|
|
+ .adapter_nr = adapter_nr,
|
|
+ .size_of_priv = sizeof(struct dvbsky_state),
|
|
+
|
|
+ .generic_bulk_ctrl_endpoint = 0x01,
|
|
+ .generic_bulk_ctrl_endpoint_response = 0x81,
|
|
+ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
|
|
+
|
|
+ .i2c_algo = &dvbsky_i2c_algo,
|
|
+ .frontend_attach = dvbsky_t330_attach,
|
|
+ .init = dvbsky_init,
|
|
+ .get_rc_config = dvbsky_get_rc_config,
|
|
+ .streaming_ctrl = dvbsky_streaming_ctrl,
|
|
+ .identify_state = dvbsky_identify_state,
|
|
+ .exit = dvbsky_exit,
|
|
+ .read_mac_address = dvbsky_read_mac_addr,
|
|
+
|
|
+ .num_adapters = 1,
|
|
+ .adapter = {
|
|
+ {
|
|
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
|
|
+ }
|
|
+ }
|
|
+};
|
|
+
|
|
+static const struct usb_device_id dvbsky_id_table[] = {
|
|
+ { DVB_USB_DEVICE(0x0572, 0x6831,
|
|
+ &dvbsky_s960_props, "DVBSky S960/S860", RC_MAP_DVBSKY) },
|
|
+ { DVB_USB_DEVICE(0x0572, 0x960c,
|
|
+ &dvbsky_s960c_props, "DVBSky S960CI", RC_MAP_DVBSKY) },
|
|
+ { DVB_USB_DEVICE(0x0572, 0x680c,
|
|
+ &dvbsky_t680c_props, "DVBSky T680CI", RC_MAP_DVBSKY) },
|
|
+ { DVB_USB_DEVICE(0x0572, 0x0320,
|
|
+ &dvbsky_t330_props, "DVBSky T330", RC_MAP_DVBSKY) },
|
|
+ { DVB_USB_DEVICE(USB_VID_TECHNOTREND,
|
|
+ USB_PID_TECHNOTREND_TVSTICK_CT2_4400,
|
|
+ &dvbsky_t330_props, "TechnoTrend TVStick CT2-4400",
|
|
+ RC_MAP_TT_1500) },
|
|
+ { DVB_USB_DEVICE(USB_VID_TECHNOTREND,
|
|
+ USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI,
|
|
+ &dvbsky_t680c_props, "TechnoTrend TT-connect CT2-4650 CI",
|
|
+ RC_MAP_TT_1500) },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(usb, dvbsky_id_table);
|
|
+
|
|
+static struct usb_driver dvbsky_usb_driver = {
|
|
+ .name = KBUILD_MODNAME,
|
|
+ .id_table = dvbsky_id_table,
|
|
+ .probe = dvb_usbv2_probe,
|
|
+ .disconnect = dvb_usbv2_disconnect,
|
|
+ .suspend = dvb_usbv2_suspend,
|
|
+ .resume = dvb_usbv2_resume,
|
|
+ .reset_resume = dvb_usbv2_reset_resume,
|
|
+ .no_dynamic_id = 1,
|
|
+ .soft_unbind = 1,
|
|
+};
|
|
+
|
|
+module_usb_driver(dvbsky_usb_driver);
|
|
+
|
|
+MODULE_AUTHOR("Max nibble <nibble.max@gmail.com>");
|
|
+MODULE_DESCRIPTION("Driver for DVBSky USB");
|
|
+MODULE_LICENSE("GPL");
|
|
--- a/drivers/media/rc/keymaps/Makefile 2013-12-12 14:38:07.000000000 +0800
|
|
+++ b/drivers/media/rc/keymaps/Makefile 2013-12-18 20:02:46.202844414 +0800
|
|
@@ -28,6 +28,7 @@
|
|
rc-dm1105-nec.o \
|
|
rc-dntv-live-dvb-t.o \
|
|
rc-dntv-live-dvbt-pro.o \
|
|
+ rc-dvbsky.o \
|
|
rc-em-terratec.o \
|
|
rc-encore-enltv2.o \
|
|
rc-encore-enltv.o \
|
|
diff -urN a/drivers/media/rc/keymaps/rc-dvbsky.c b/drivers/media/rc/keymaps/rc-dvbsky.c
|
|
--- a/drivers/media/rc/keymaps/rc-dvbsky.c 1970-01-01 08:00:00.000000000 +0800
|
|
+++ b/drivers/media/rc/keymaps/rc-dvbsky.c 2013-12-18 20:02:49.870844372 +0800
|
|
@@ -0,0 +1,78 @@
|
|
+/* rc-dvbsky.c - Keytable for Dvbsky Remote Controllers
|
|
+ *
|
|
+ * keymap imported from ir-keymaps.c
|
|
+ *
|
|
+ *
|
|
+ * Copyright (c) 2010-2012 by Nibble Max <nibble.max@gmail.com>
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include <media/rc-map.h>
|
|
+#include <linux/module.h>
|
|
+/*
|
|
+ * This table contains the complete RC5 code, instead of just the data part
|
|
+ */
|
|
+
|
|
+static struct rc_map_table rc5_dvbsky[] = {
|
|
+ { 0x0000, KEY_0 },
|
|
+ { 0x0001, KEY_1 },
|
|
+ { 0x0002, KEY_2 },
|
|
+ { 0x0003, KEY_3 },
|
|
+ { 0x0004, KEY_4 },
|
|
+ { 0x0005, KEY_5 },
|
|
+ { 0x0006, KEY_6 },
|
|
+ { 0x0007, KEY_7 },
|
|
+ { 0x0008, KEY_8 },
|
|
+ { 0x0009, KEY_9 },
|
|
+ { 0x000a, KEY_MUTE },
|
|
+ { 0x000d, KEY_OK },
|
|
+ { 0x000b, KEY_STOP },
|
|
+ { 0x000c, KEY_EXIT },
|
|
+ { 0x000e, KEY_CAMERA }, /*Snap shot*/
|
|
+ { 0x000f, KEY_SUBTITLE }, /*PIP*/
|
|
+ { 0x0010, KEY_VOLUMEUP },
|
|
+ { 0x0011, KEY_VOLUMEDOWN },
|
|
+ { 0x0012, KEY_FAVORITES },
|
|
+ { 0x0013, KEY_LIST }, /*Info*/
|
|
+ { 0x0016, KEY_PAUSE },
|
|
+ { 0x0017, KEY_PLAY },
|
|
+ { 0x001f, KEY_RECORD },
|
|
+ { 0x0020, KEY_CHANNELDOWN },
|
|
+ { 0x0021, KEY_CHANNELUP },
|
|
+ { 0x0025, KEY_POWER2 },
|
|
+ { 0x0026, KEY_REWIND },
|
|
+ { 0x0027, KEY_FASTFORWARD },
|
|
+ { 0x0029, KEY_LAST },
|
|
+ { 0x002b, KEY_MENU },
|
|
+ { 0x002c, KEY_EPG },
|
|
+ { 0x002d, KEY_ZOOM },
|
|
+};
|
|
+
|
|
+static struct rc_map_list rc5_dvbsky_map = {
|
|
+ .map = {
|
|
+ .scan = rc5_dvbsky,
|
|
+ .size = ARRAY_SIZE(rc5_dvbsky),
|
|
+ .rc_type = RC_TYPE_RC5,
|
|
+ .name = RC_MAP_DVBSKY,
|
|
+ }
|
|
+};
|
|
+
|
|
+static int __init init_rc_map_rc5_dvbsky(void)
|
|
+{
|
|
+ return rc_map_register(&rc5_dvbsky_map);
|
|
+}
|
|
+
|
|
+static void __exit exit_rc_map_rc5_dvbsky(void)
|
|
+{
|
|
+ rc_map_unregister(&rc5_dvbsky_map);
|
|
+}
|
|
+
|
|
+module_init(init_rc_map_rc5_dvbsky)
|
|
+module_exit(exit_rc_map_rc5_dvbsky)
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_AUTHOR("Nibble Max <nibble.max@gmail.com>");
|
|
--- a/include/media/rc-map.h 2013-12-12 14:38:07.000000000 +0800
|
|
+++ b/include/media/rc-map.h 2013-12-18 19:59:03.122846921 +0800
|
|
@@ -119,6 +119,7 @@
|
|
#define RC_MAP_DM1105_NEC "rc-dm1105-nec"
|
|
#define RC_MAP_DNTV_LIVE_DVBT_PRO "rc-dntv-live-dvbt-pro"
|
|
#define RC_MAP_DNTV_LIVE_DVB_T "rc-dntv-live-dvb-t"
|
|
+#define RC_MAP_DVBSKY "rc-dvbsky"
|
|
#define RC_MAP_EMPTY "rc-empty"
|
|
#define RC_MAP_EM_TERRATEC "rc-em-terratec"
|
|
#define RC_MAP_ENCORE_ENLTV2 "rc-encore-enltv2"
|
|
--- a/drivers/media/dvb-frontends/ts2020.c
|
|
+++ b/drivers/media/dvb-frontends/ts2020.c
|
|
@@ -26,12 +26,23 @@
|
|
#define FREQ_OFFSET_LOW_SYM_RATE 3000
|
|
|
|
struct ts2020_priv {
|
|
+ struct dvb_frontend *fe;
|
|
/* i2c details */
|
|
int i2c_address;
|
|
struct i2c_adapter *i2c;
|
|
- u8 clk_out_div;
|
|
+ u8 clk_out:2;
|
|
+ u8 clk_out_div:5;
|
|
u32 frequency;
|
|
u32 frequency_div;
|
|
+#define TS2020_M88TS2020 0
|
|
+#define TS2020_M88TS2022 1
|
|
+ u8 tuner;
|
|
+ u8 loop_through:1;
|
|
+};
|
|
+
|
|
+struct ts2020_reg_val {
|
|
+ u8 reg;
|
|
+ u8 val;
|
|
};
|
|
|
|
static int ts2020_release(struct dvb_frontend *fe)
|
|
@@ -112,40 +123,77 @@
|
|
static int ts2020_sleep(struct dvb_frontend *fe)
|
|
{
|
|
struct ts2020_priv *priv = fe->tuner_priv;
|
|
- int ret;
|
|
- u8 buf[] = { 10, 0 };
|
|
- struct i2c_msg msg = {
|
|
- .addr = priv->i2c_address,
|
|
- .flags = 0,
|
|
- .buf = buf,
|
|
- .len = 2
|
|
- };
|
|
+ u8 u8tmp;
|
|
|
|
- if (fe->ops.i2c_gate_ctrl)
|
|
- fe->ops.i2c_gate_ctrl(fe, 1);
|
|
-
|
|
- ret = i2c_transfer(priv->i2c, &msg, 1);
|
|
- if (ret != 1)
|
|
- printk(KERN_ERR "%s: i2c error\n", __func__);
|
|
-
|
|
- if (fe->ops.i2c_gate_ctrl)
|
|
- fe->ops.i2c_gate_ctrl(fe, 0);
|
|
+ if (priv->tuner == TS2020_M88TS2020)
|
|
+ u8tmp = 0x0a; /* XXX: probably wrong */
|
|
+ else
|
|
+ u8tmp = 0x00;
|
|
|
|
- return (ret == 1) ? 0 : ret;
|
|
+ return ts2020_writereg(fe, u8tmp, 0x00);
|
|
}
|
|
|
|
static int ts2020_init(struct dvb_frontend *fe)
|
|
{
|
|
struct ts2020_priv *priv = fe->tuner_priv;
|
|
+ int i;
|
|
+ u8 u8tmp;
|
|
+
|
|
+ if (priv->tuner == TS2020_M88TS2020) {
|
|
+ ts2020_writereg(fe, 0x42, 0x73);
|
|
+ ts2020_writereg(fe, 0x05, priv->clk_out_div);
|
|
+ ts2020_writereg(fe, 0x20, 0x27);
|
|
+ ts2020_writereg(fe, 0x07, 0x02);
|
|
+ ts2020_writereg(fe, 0x11, 0xff);
|
|
+ ts2020_writereg(fe, 0x60, 0xf9);
|
|
+ ts2020_writereg(fe, 0x08, 0x01);
|
|
+ ts2020_writereg(fe, 0x00, 0x41);
|
|
+ } else {
|
|
+ static const struct ts2020_reg_val reg_vals[] = {
|
|
+ {0x7d, 0x9d},
|
|
+ {0x7c, 0x9a},
|
|
+ {0x7a, 0x76},
|
|
+ {0x3b, 0x01},
|
|
+ {0x63, 0x88},
|
|
+ {0x61, 0x85},
|
|
+ {0x22, 0x30},
|
|
+ {0x30, 0x40},
|
|
+ {0x20, 0x23},
|
|
+ {0x24, 0x02},
|
|
+ {0x12, 0xa0},
|
|
+ };
|
|
+
|
|
+ ts2020_writereg(fe, 0x00, 0x01);
|
|
+ ts2020_writereg(fe, 0x00, 0x03);
|
|
+
|
|
+ switch (priv->clk_out) {
|
|
+ case TS2020_CLK_OUT_DISABLED:
|
|
+ u8tmp = 0x60;
|
|
+ break;
|
|
+ case TS2020_CLK_OUT_ENABLED:
|
|
+ u8tmp = 0x70;
|
|
+ ts2020_writereg(fe, 0x05, priv->clk_out_div);
|
|
+ break;
|
|
+ case TS2020_CLK_OUT_ENABLED_XTALOUT:
|
|
+ u8tmp = 0x6c;
|
|
+ break;
|
|
+ default:
|
|
+ u8tmp = 0x60;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ts2020_writereg(fe, 0x42, u8tmp);
|
|
+
|
|
+ if (priv->loop_through)
|
|
+ u8tmp = 0xec;
|
|
+ else
|
|
+ u8tmp = 0x6c;
|
|
|
|
- ts2020_writereg(fe, 0x42, 0x73);
|
|
- ts2020_writereg(fe, 0x05, priv->clk_out_div);
|
|
- ts2020_writereg(fe, 0x20, 0x27);
|
|
- ts2020_writereg(fe, 0x07, 0x02);
|
|
- ts2020_writereg(fe, 0x11, 0xff);
|
|
- ts2020_writereg(fe, 0x60, 0xf9);
|
|
- ts2020_writereg(fe, 0x08, 0x01);
|
|
- ts2020_writereg(fe, 0x00, 0x41);
|
|
+ ts2020_writereg(fe, 0x62, u8tmp);
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(reg_vals); i++)
|
|
+ ts2020_writereg(fe, reg_vals[i].reg, reg_vals[i].val);
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
@@ -203,7 +251,14 @@
|
|
ndiv = ndiv + ndiv % 2;
|
|
ndiv = ndiv - 1024;
|
|
|
|
- ret = ts2020_writereg(fe, 0x10, 0x80 | lo);
|
|
+ if (priv->tuner == TS2020_M88TS2020) {
|
|
+ lpf_coeff = 2766;
|
|
+ ret = ts2020_writereg(fe, 0x10, 0x80 | lo);
|
|
+ } else {
|
|
+ lpf_coeff = 3200;
|
|
+ ret = ts2020_writereg(fe, 0x10, 0x0b);
|
|
+ ret |= ts2020_writereg(fe, 0x11, 0x40);
|
|
+ }
|
|
|
|
/* Set frequency divider */
|
|
ret |= ts2020_writereg(fe, 0x01, (ndiv >> 8) & 0xf);
|
|
@@ -220,7 +275,8 @@
|
|
ret |= ts2020_tuner_gate_ctrl(fe, 0x08);
|
|
|
|
/* Tuner RF */
|
|
- ret |= ts2020_set_tuner_rf(fe);
|
|
+ if (priv->tuner == TS2020_M88TS2020)
|
|
+ ret |= ts2020_set_tuner_rf(fe);
|
|
|
|
gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000;
|
|
ret |= ts2020_writereg(fe, 0x04, gdiv28 & 0xff);
|
|
@@ -228,6 +284,15 @@
|
|
if (ret < 0)
|
|
return -ENODEV;
|
|
|
|
+ if (priv->tuner == TS2020_M88TS2022) {
|
|
+ ret = ts2020_writereg(fe, 0x25, 0x00);
|
|
+ ret |= ts2020_writereg(fe, 0x27, 0x70);
|
|
+ ret |= ts2020_writereg(fe, 0x41, 0x09);
|
|
+ ret |= ts2020_writereg(fe, 0x08, 0x0b);
|
|
+ if (ret < 0)
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
value = ts2020_readreg(fe, 0x26);
|
|
|
|
f3db = (symbol_rate * 135) / 200 + 2000;
|
|
@@ -243,8 +308,6 @@
|
|
if (mlpf_max > 63)
|
|
mlpf_max = 63;
|
|
|
|
- lpf_coeff = 2766;
|
|
-
|
|
nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
|
|
(TS2020_XTAL_FREQ / 1000) + 1) / 2;
|
|
if (nlpf > 23)
|
|
@@ -285,6 +348,13 @@
|
|
{
|
|
struct ts2020_priv *priv = fe->tuner_priv;
|
|
*frequency = priv->frequency;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
|
|
+{
|
|
+ *frequency = 0; /* Zero-IF */
|
|
return 0;
|
|
}
|
|
|
|
@@ -324,6 +394,7 @@
|
|
.sleep = ts2020_sleep,
|
|
.set_params = ts2020_set_params,
|
|
.get_frequency = ts2020_get_frequency,
|
|
+ .get_if_frequency = ts2020_get_if_frequency,
|
|
.get_rf_strength = ts2020_read_signal_strength,
|
|
};
|
|
|
|
@@ -340,8 +411,10 @@
|
|
|
|
priv->i2c_address = config->tuner_address;
|
|
priv->i2c = i2c;
|
|
+ priv->clk_out = config->clk_out;
|
|
priv->clk_out_div = config->clk_out_div;
|
|
priv->frequency_div = config->frequency_div;
|
|
+ priv->fe = fe;
|
|
fe->tuner_priv = priv;
|
|
|
|
if (!priv->frequency_div)
|
|
@@ -358,9 +431,13 @@
|
|
|
|
/* Check the tuner version */
|
|
buf = ts2020_readreg(fe, 0x00);
|
|
- if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81))
|
|
+ if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81)) {
|
|
printk(KERN_INFO "%s: Find tuner TS2020!\n", __func__);
|
|
- else {
|
|
+ priv->tuner = TS2020_M88TS2020;
|
|
+ } else if ((buf == 0x83) || (buf == 0xc3)) {
|
|
+ printk(KERN_INFO "%s: Find tuner TS2022!\n", __func__);
|
|
+ priv->tuner = TS2020_M88TS2022;
|
|
+ } else {
|
|
printk(KERN_ERR "%s: Read tuner reg[0] = %d\n", __func__, buf);
|
|
kfree(priv);
|
|
return NULL;
|
|
@@ -373,6 +450,165 @@
|
|
}
|
|
EXPORT_SYMBOL(ts2020_attach);
|
|
|
|
+static int ts2020_probe(struct i2c_client *client,
|
|
+ const struct i2c_device_id *id)
|
|
+{
|
|
+ struct ts2020_config *pdata = client->dev.platform_data;
|
|
+ struct dvb_frontend *fe = pdata->fe;
|
|
+ struct ts2020_priv *dev;
|
|
+ int ret;
|
|
+ u8 u8tmp;
|
|
+ unsigned int utmp;
|
|
+ char *chip_str;
|
|
+
|
|
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
+ if (!dev) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ dev->i2c = client->adapter;
|
|
+ dev->i2c_address = client->addr;
|
|
+ dev->clk_out = pdata->clk_out;
|
|
+ dev->clk_out_div = pdata->clk_out_div;
|
|
+ dev->frequency_div = pdata->frequency_div;
|
|
+ dev->fe = fe;
|
|
+ fe->tuner_priv = dev;
|
|
+
|
|
+ /* check if the tuner is there */
|
|
+ ret = ts2020_readreg(fe, 0x00);
|
|
+ if (ret < 0)
|
|
+ goto err;
|
|
+ utmp = ret;
|
|
+
|
|
+ if ((utmp & 0x03) == 0x00) {
|
|
+ ret = ts2020_writereg(fe, 0x00, 0x01);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ usleep_range(2000, 50000);
|
|
+ }
|
|
+
|
|
+ ret = ts2020_writereg(fe, 0x00, 0x03);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ usleep_range(2000, 50000);
|
|
+
|
|
+ ret = ts2020_readreg(fe, 0x00);
|
|
+ if (ret < 0)
|
|
+ goto err;
|
|
+ utmp = ret;
|
|
+
|
|
+ dev_dbg(&client->dev, "chip_id=%02x\n", utmp);
|
|
+
|
|
+ switch (utmp) {
|
|
+ case 0x01:
|
|
+ case 0x41:
|
|
+ case 0x81:
|
|
+ dev->tuner = TS2020_M88TS2020;
|
|
+ chip_str = "TS2020";
|
|
+ if (!dev->frequency_div)
|
|
+ dev->frequency_div = 1060000;
|
|
+ break;
|
|
+ case 0xc3:
|
|
+ case 0x83:
|
|
+ dev->tuner = TS2020_M88TS2022;
|
|
+ chip_str = "TS2022";
|
|
+ if (!dev->frequency_div)
|
|
+ dev->frequency_div = 1103000;
|
|
+ break;
|
|
+ default:
|
|
+ ret = -ENODEV;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ if (dev->tuner == TS2020_M88TS2022) {
|
|
+ switch (dev->clk_out) {
|
|
+ case TS2020_CLK_OUT_DISABLED:
|
|
+ u8tmp = 0x60;
|
|
+ break;
|
|
+ case TS2020_CLK_OUT_ENABLED:
|
|
+ u8tmp = 0x70;
|
|
+ ret = ts2020_writereg(fe, 0x05, dev->clk_out_div);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ break;
|
|
+ case TS2020_CLK_OUT_ENABLED_XTALOUT:
|
|
+ u8tmp = 0x6c;
|
|
+ break;
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ ret = ts2020_writereg(fe, 0x42, u8tmp);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ if (dev->loop_through)
|
|
+ u8tmp = 0xec;
|
|
+ else
|
|
+ u8tmp = 0x6c;
|
|
+
|
|
+ ret = ts2020_writereg(fe, 0x62, u8tmp);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ /* sleep */
|
|
+ ret = ts2020_writereg(fe, 0x00, 0x00);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ dev_info(&client->dev,
|
|
+ "Montage Technology %s successfully identified\n", chip_str);
|
|
+
|
|
+ memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops,
|
|
+ sizeof(struct dvb_tuner_ops));
|
|
+ fe->ops.tuner_ops.release = NULL;
|
|
+
|
|
+ i2c_set_clientdata(client, dev);
|
|
+ return 0;
|
|
+err:
|
|
+ dev_dbg(&client->dev, "failed=%d\n", ret);
|
|
+ kfree(dev);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ts2020_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct ts2020_priv *dev = i2c_get_clientdata(client);
|
|
+ struct dvb_frontend *fe = dev->fe;
|
|
+
|
|
+ dev_dbg(&client->dev, "\n");
|
|
+
|
|
+ memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
|
|
+ fe->tuner_priv = NULL;
|
|
+ kfree(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct i2c_device_id ts2020_id_table[] = {
|
|
+ {"ts2020", 0},
|
|
+ {"ts2022", 0},
|
|
+ {}
|
|
+};
|
|
+MODULE_DEVICE_TABLE(i2c, ts2020_id_table);
|
|
+
|
|
+static struct i2c_driver ts2020_driver = {
|
|
+ .driver = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .name = "ts2020",
|
|
+ },
|
|
+ .probe = ts2020_probe,
|
|
+ .remove = ts2020_remove,
|
|
+ .id_table = ts2020_id_table,
|
|
+};
|
|
+
|
|
+module_i2c_driver(ts2020_driver);
|
|
+
|
|
MODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>");
|
|
MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module");
|
|
MODULE_LICENSE("GPL");
|
|
--- a/drivers/media/dvb-frontends/ts2020.h
|
|
+++ b/drivers/media/dvb-frontends/ts2020.h
|
|
@@ -27,11 +27,34 @@
|
|
|
|
struct ts2020_config {
|
|
u8 tuner_address;
|
|
- u8 clk_out_div;
|
|
u32 frequency_div;
|
|
+
|
|
+ /*
|
|
+ * RF loop-through
|
|
+ */
|
|
+ u8 loop_through:1;
|
|
+
|
|
+ /*
|
|
+ * clock output
|
|
+ */
|
|
+#define TS2020_CLK_OUT_DISABLED 0
|
|
+#define TS2020_CLK_OUT_ENABLED 1
|
|
+#define TS2020_CLK_OUT_ENABLED_XTALOUT 2
|
|
+ u8 clk_out:2;
|
|
+
|
|
+ /*
|
|
+ * clock output divider
|
|
+ * 1 - 31
|
|
+ */
|
|
+ u8 clk_out_div:5;
|
|
+
|
|
+ /*
|
|
+ * pointer to DVB frontend
|
|
+ */
|
|
+ struct dvb_frontend *fe;
|
|
};
|
|
|
|
-#if IS_ENABLED(CONFIG_DVB_TS2020)
|
|
+#if IS_ENABLED(CONFIG_DVB_TS2020)
|
|
|
|
extern struct dvb_frontend *ts2020_attach(
|
|
struct dvb_frontend *fe,
|
|
--- a/drivers/media/dvb-frontends/m88ds3103.h
|
|
+++ b/drivers/media/dvb-frontends/m88ds3103.h
|
|
@@ -47,14 +47,23 @@
|
|
*/
|
|
#define M88DS3103_TS_SERIAL 0 /* TS output pin D0, normal */
|
|
#define M88DS3103_TS_SERIAL_D7 1 /* TS output pin D7 */
|
|
-#define M88DS3103_TS_PARALLEL 2 /* 24 MHz, normal */
|
|
-#define M88DS3103_TS_PARALLEL_12 3 /* 12 MHz */
|
|
-#define M88DS3103_TS_PARALLEL_16 4 /* 16 MHz */
|
|
-#define M88DS3103_TS_PARALLEL_19_2 5 /* 19.2 MHz */
|
|
-#define M88DS3103_TS_CI 6 /* 6 MHz */
|
|
+#define M88DS3103_TS_PARALLEL 2 /* TS Parallel mode */
|
|
+#define M88DS3103_TS_CI 3 /* TS CI Mode */
|
|
u8 ts_mode;
|
|
|
|
/*
|
|
+ * TS clk in KHz
|
|
+ * Default: 0.
|
|
+ */
|
|
+ u32 ts_clk;
|
|
+
|
|
+ /*
|
|
+ * TS clk polarity.
|
|
+ * Default: 0. 1-active at falling edge; 0-active at rising edge.
|
|
+ */
|
|
+ u8 ts_clk_pol:1;
|
|
+
|
|
+ /*
|
|
* spectrum inversion
|
|
* Default: 0
|
|
*/
|
|
@@ -86,6 +95,22 @@
|
|
* Default: none, must set
|
|
*/
|
|
u8 agc;
|
|
+
|
|
+ /*
|
|
+ * LNB H/V pin polarity
|
|
+ * Default: 0.
|
|
+ * 1: pin high set to VOLTAGE_13, pin low to set VOLTAGE_18.
|
|
+ * 0: pin high set to VOLTAGE_18, pin low to set VOLTAGE_13.
|
|
+ */
|
|
+ u8 lnb_hv_pol:1;
|
|
+
|
|
+ /*
|
|
+ * LNB enable pin polarity
|
|
+ * Default: 0.
|
|
+ * 1: pin high to enable, pin low to disable.
|
|
+ * 0: pin high to disable, pin low to enable.
|
|
+ */
|
|
+ u8 lnb_en_pol:1;
|
|
};
|
|
|
|
/*
|
|
--- a/drivers/media/dvb-frontends/m88ds3103.c
|
|
+++ b/drivers/media/dvb-frontends/m88ds3103.c
|
|
@@ -1,5 +1,5 @@
|
|
/*
|
|
- * Montage M88DS3103 demodulator driver
|
|
+ * Montage M88DS3103/M88RS6000 demodulator driver
|
|
*
|
|
* Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
|
|
*
|
|
@@ -159,9 +159,10 @@
|
|
{
|
|
int ret, i, j;
|
|
u8 buf[83];
|
|
+
|
|
dev_dbg(&priv->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len);
|
|
|
|
- if (tab_len > 83) {
|
|
+ if (tab_len > 86) {
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
@@ -244,11 +245,12 @@
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
int ret, len;
|
|
const struct m88ds3103_reg_val *init;
|
|
- u8 u8tmp, u8tmp1, u8tmp2;
|
|
- u8 buf[2];
|
|
- u16 u16tmp, divide_ratio;
|
|
- u32 tuner_frequency, target_mclk, ts_clk;
|
|
+ u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0; /* silence compiler warning */
|
|
+ u8 buf[3];
|
|
+ u16 u16tmp, divide_ratio = 0;
|
|
+ u32 tuner_frequency, target_mclk;
|
|
s32 s32tmp;
|
|
+
|
|
dev_dbg(&priv->i2c->dev,
|
|
"%s: delivery_system=%d modulation=%d frequency=%d symbol_rate=%d inversion=%d pilot=%d rolloff=%d\n",
|
|
__func__, c->delivery_system,
|
|
@@ -260,6 +262,22 @@
|
|
goto err;
|
|
}
|
|
|
|
+ /* reset */
|
|
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x80);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ /* Disable demod clock path */
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
|
|
+ ret = m88ds3103_wr_reg(priv, 0x06, 0xe0);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
/* program tuner */
|
|
if (fe->ops.tuner_ops.set_params) {
|
|
ret = fe->ops.tuner_ops.set_params(fe);
|
|
@@ -271,54 +289,53 @@
|
|
ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_frequency);
|
|
if (ret)
|
|
goto err;
|
|
+ } else {
|
|
+ /*
|
|
+ * Use nominal target frequency as tuner driver does not provide
|
|
+ * actual frequency used. Carrier offset calculation is not
|
|
+ * valid.
|
|
+ */
|
|
+ tuner_frequency = c->frequency;
|
|
}
|
|
|
|
- /* reset */
|
|
- ret = m88ds3103_wr_reg(priv, 0x07, 0x80);
|
|
- if (ret)
|
|
- goto err;
|
|
-
|
|
- ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
|
|
- if (ret)
|
|
- goto err;
|
|
-
|
|
- ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
|
|
- if (ret)
|
|
- goto err;
|
|
+ /* select M88RS6000 demod main mclk and ts mclk from tuner die. */
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
|
|
+ if (c->symbol_rate > 45010000)
|
|
+ priv->mclk_khz = 110250;
|
|
+ else
|
|
+ priv->mclk_khz = 96000;
|
|
|
|
- ret = m88ds3103_wr_reg(priv, 0x00, 0x01);
|
|
- if (ret)
|
|
- goto err;
|
|
+ if (c->delivery_system == SYS_DVBS)
|
|
+ target_mclk = 96000;
|
|
+ else
|
|
+ target_mclk = 144000;
|
|
|
|
- switch (c->delivery_system) {
|
|
- case SYS_DVBS:
|
|
- len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
|
|
- init = m88ds3103_dvbs_init_reg_vals;
|
|
- target_mclk = 96000;
|
|
- break;
|
|
- case SYS_DVBS2:
|
|
- len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
|
|
- init = m88ds3103_dvbs2_init_reg_vals;
|
|
+ /* Enable demod clock path */
|
|
+ ret = m88ds3103_wr_reg(priv, 0x06, 0x00);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ usleep_range(10000, 20000);
|
|
+ } else {
|
|
+ /* set M88DS3103 mclk and ts mclk. */
|
|
+ priv->mclk_khz = 96000;
|
|
|
|
switch (priv->cfg->ts_mode) {
|
|
case M88DS3103_TS_SERIAL:
|
|
case M88DS3103_TS_SERIAL_D7:
|
|
- if (c->symbol_rate < 18000000)
|
|
- target_mclk = 96000;
|
|
- else
|
|
- target_mclk = 144000;
|
|
+ target_mclk = priv->cfg->ts_clk;
|
|
break;
|
|
case M88DS3103_TS_PARALLEL:
|
|
- case M88DS3103_TS_PARALLEL_12:
|
|
- case M88DS3103_TS_PARALLEL_16:
|
|
- case M88DS3103_TS_PARALLEL_19_2:
|
|
case M88DS3103_TS_CI:
|
|
- if (c->symbol_rate < 18000000)
|
|
+ if (c->delivery_system == SYS_DVBS)
|
|
target_mclk = 96000;
|
|
- else if (c->symbol_rate < 28000000)
|
|
- target_mclk = 144000;
|
|
- else
|
|
- target_mclk = 192000;
|
|
+ else {
|
|
+ if (c->symbol_rate < 18000000)
|
|
+ target_mclk = 96000;
|
|
+ else if (c->symbol_rate < 28000000)
|
|
+ target_mclk = 144000;
|
|
+ else
|
|
+ target_mclk = 192000;
|
|
+ }
|
|
break;
|
|
default:
|
|
dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n",
|
|
@@ -326,6 +343,55 @@
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
+
|
|
+ switch (target_mclk) {
|
|
+ case 96000:
|
|
+ u8tmp1 = 0x02; /* 0b10 */
|
|
+ u8tmp2 = 0x01; /* 0b01 */
|
|
+ break;
|
|
+ case 144000:
|
|
+ u8tmp1 = 0x00; /* 0b00 */
|
|
+ u8tmp2 = 0x01; /* 0b01 */
|
|
+ break;
|
|
+ case 192000:
|
|
+ u8tmp1 = 0x03; /* 0b11 */
|
|
+ u8tmp2 = 0x00; /* 0b00 */
|
|
+ break;
|
|
+ }
|
|
+ ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = m88ds3103_wr_reg(priv, 0x00, 0x01);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ switch (c->delivery_system) {
|
|
+ case SYS_DVBS:
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
|
|
+ len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals);
|
|
+ init = m88rs6000_dvbs_init_reg_vals;
|
|
+ } else {
|
|
+ len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
|
|
+ init = m88ds3103_dvbs_init_reg_vals;
|
|
+ }
|
|
+ break;
|
|
+ case SYS_DVBS2:
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
|
|
+ len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals);
|
|
+ init = m88rs6000_dvbs2_init_reg_vals;
|
|
+ } else {
|
|
+ len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
|
|
+ init = m88ds3103_dvbs2_init_reg_vals;
|
|
+ }
|
|
break;
|
|
default:
|
|
dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
|
|
@@ -341,37 +407,44 @@
|
|
goto err;
|
|
}
|
|
|
|
- u8tmp1 = 0; /* silence compiler warning */
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
|
|
+ if ((c->delivery_system == SYS_DVBS2)
|
|
+ && ((c->symbol_rate / 1000) <= 5000)) {
|
|
+ ret = m88ds3103_wr_reg(priv, 0xc0, 0x04);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ buf[0] = 0x09;
|
|
+ buf[1] = 0x22;
|
|
+ buf[2] = 0x88;
|
|
+ ret = m88ds3103_wr_regs(priv, 0x8a, buf, 3);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+ ret = m88ds3103_wr_reg_mask(priv, 0x9d, 0x08, 0x08);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ ret = m88ds3103_wr_reg(priv, 0xf1, 0x01);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ ret = m88ds3103_wr_reg_mask(priv, 0x30, 0x80, 0x80);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
switch (priv->cfg->ts_mode) {
|
|
case M88DS3103_TS_SERIAL:
|
|
u8tmp1 = 0x00;
|
|
- ts_clk = 0;
|
|
- u8tmp = 0x46;
|
|
+ u8tmp = 0x06;
|
|
break;
|
|
case M88DS3103_TS_SERIAL_D7:
|
|
u8tmp1 = 0x20;
|
|
- ts_clk = 0;
|
|
- u8tmp = 0x46;
|
|
+ u8tmp = 0x06;
|
|
break;
|
|
case M88DS3103_TS_PARALLEL:
|
|
- ts_clk = 24000;
|
|
- u8tmp = 0x42;
|
|
- break;
|
|
- case M88DS3103_TS_PARALLEL_12:
|
|
- ts_clk = 12000;
|
|
- u8tmp = 0x42;
|
|
- break;
|
|
- case M88DS3103_TS_PARALLEL_16:
|
|
- ts_clk = 16000;
|
|
- u8tmp = 0x42;
|
|
- break;
|
|
- case M88DS3103_TS_PARALLEL_19_2:
|
|
- ts_clk = 19200;
|
|
- u8tmp = 0x42;
|
|
+ u8tmp = 0x02;
|
|
break;
|
|
case M88DS3103_TS_CI:
|
|
- ts_clk = 6000;
|
|
- u8tmp = 0x43;
|
|
+ u8tmp = 0x03;
|
|
break;
|
|
default:
|
|
dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n", __func__);
|
|
@@ -379,6 +452,9 @@
|
|
goto err;
|
|
}
|
|
|
|
+ if (priv->cfg->ts_clk_pol)
|
|
+ u8tmp |= 0x40;
|
|
+
|
|
/* TS mode */
|
|
ret = m88ds3103_wr_reg(priv, 0xfd, u8tmp);
|
|
if (ret)
|
|
@@ -390,21 +466,20 @@
|
|
ret = m88ds3103_wr_reg_mask(priv, 0x29, u8tmp1, 0x20);
|
|
if (ret)
|
|
goto err;
|
|
- }
|
|
-
|
|
- if (ts_clk) {
|
|
- divide_ratio = DIV_ROUND_UP(target_mclk, ts_clk);
|
|
- u8tmp1 = divide_ratio / 2;
|
|
- u8tmp2 = DIV_ROUND_UP(divide_ratio, 2);
|
|
- } else {
|
|
- divide_ratio = 0;
|
|
u8tmp1 = 0;
|
|
u8tmp2 = 0;
|
|
+ break;
|
|
+ default:
|
|
+ if (priv->cfg->ts_clk) {
|
|
+ divide_ratio = DIV_ROUND_UP(target_mclk, priv->cfg->ts_clk);
|
|
+ u8tmp1 = divide_ratio / 2;
|
|
+ u8tmp2 = DIV_ROUND_UP(divide_ratio, 2);
|
|
+ }
|
|
}
|
|
|
|
dev_dbg(&priv->i2c->dev,
|
|
"%s: target_mclk=%d ts_clk=%d divide_ratio=%d\n",
|
|
- __func__, target_mclk, ts_clk, divide_ratio);
|
|
+ __func__, target_mclk, priv->cfg->ts_clk, divide_ratio);
|
|
|
|
u8tmp1--;
|
|
u8tmp2--;
|
|
@@ -427,41 +502,6 @@
|
|
if (ret)
|
|
goto err;
|
|
|
|
- switch (target_mclk) {
|
|
- case 72000:
|
|
- u8tmp1 = 0x00; /* 0b00 */
|
|
- u8tmp2 = 0x03; /* 0b11 */
|
|
- break;
|
|
- case 96000:
|
|
- u8tmp1 = 0x02; /* 0b10 */
|
|
- u8tmp2 = 0x01; /* 0b01 */
|
|
- break;
|
|
- case 115200:
|
|
- u8tmp1 = 0x01; /* 0b01 */
|
|
- u8tmp2 = 0x01; /* 0b01 */
|
|
- break;
|
|
- case 144000:
|
|
- u8tmp1 = 0x00; /* 0b00 */
|
|
- u8tmp2 = 0x01; /* 0b01 */
|
|
- break;
|
|
- case 192000:
|
|
- u8tmp1 = 0x03; /* 0b11 */
|
|
- u8tmp2 = 0x00; /* 0b00 */
|
|
- break;
|
|
- default:
|
|
- dev_dbg(&priv->i2c->dev, "%s: invalid target_mclk\n", __func__);
|
|
- ret = -EINVAL;
|
|
- goto err;
|
|
- }
|
|
-
|
|
- ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0);
|
|
- if (ret)
|
|
- goto err;
|
|
-
|
|
- ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0);
|
|
- if (ret)
|
|
- goto err;
|
|
-
|
|
if (c->symbol_rate <= 3000000)
|
|
u8tmp = 0x20;
|
|
else if (c->symbol_rate <= 10000000)
|
|
@@ -485,7 +525,7 @@
|
|
if (ret)
|
|
goto err;
|
|
|
|
- u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, M88DS3103_MCLK_KHZ / 2);
|
|
+ u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, priv->mclk_khz / 2);
|
|
buf[0] = (u16tmp >> 0) & 0xff;
|
|
buf[1] = (u16tmp >> 8) & 0xff;
|
|
ret = m88ds3103_wr_regs(priv, 0x61, buf, 2);
|
|
@@ -508,7 +548,7 @@
|
|
(tuner_frequency - c->frequency));
|
|
|
|
s32tmp = 0x10000 * (tuner_frequency - c->frequency);
|
|
- s32tmp = DIV_ROUND_CLOSEST(s32tmp, M88DS3103_MCLK_KHZ);
|
|
+ s32tmp = DIV_ROUND_CLOSEST(s32tmp, priv->mclk_khz);
|
|
if (s32tmp < 0)
|
|
s32tmp += 0x10000;
|
|
|
|
@@ -539,8 +579,9 @@
|
|
struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
int ret, len, remaining;
|
|
const struct firmware *fw = NULL;
|
|
- u8 *fw_file = M88DS3103_FIRMWARE;
|
|
+ u8 *fw_file;
|
|
u8 u8tmp;
|
|
+
|
|
dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
|
|
|
|
/* set cold state by default */
|
|
@@ -559,15 +600,6 @@
|
|
if (ret)
|
|
goto err;
|
|
|
|
- /* reset */
|
|
- ret = m88ds3103_wr_reg(priv, 0x07, 0x60);
|
|
- if (ret)
|
|
- goto err;
|
|
-
|
|
- ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
|
|
- if (ret)
|
|
- goto err;
|
|
-
|
|
/* firmware status */
|
|
ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp);
|
|
if (ret)
|
|
@@ -578,14 +610,27 @@
|
|
if (u8tmp)
|
|
goto skip_fw_download;
|
|
|
|
+ /* global reset, global diseqc reset, golbal fec reset */
|
|
+ ret = m88ds3103_wr_reg(priv, 0x07, 0xe0);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
/* cold state - try to download firmware */
|
|
dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state\n",
|
|
KBUILD_MODNAME, m88ds3103_ops.info.name);
|
|
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID)
|
|
+ fw_file = M88RS6000_FIRMWARE;
|
|
+ else
|
|
+ fw_file = M88DS3103_FIRMWARE;
|
|
/* request the firmware, this will block and timeout */
|
|
ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
|
|
if (ret) {
|
|
- dev_err(&priv->i2c->dev, "%s: firmare file '%s' not found\n",
|
|
+ dev_err(&priv->i2c->dev, "%s: firmware file '%s' not found\n",
|
|
KBUILD_MODNAME, fw_file);
|
|
goto err;
|
|
}
|
|
@@ -595,7 +640,7 @@
|
|
|
|
ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
|
|
if (ret)
|
|
- goto err;
|
|
+ goto error_fw_release;
|
|
|
|
for (remaining = fw->size; remaining > 0;
|
|
remaining -= (priv->cfg->i2c_wr_max - 1)) {
|
|
@@ -609,13 +654,13 @@
|
|
dev_err(&priv->i2c->dev,
|
|
"%s: firmware download failed=%d\n",
|
|
KBUILD_MODNAME, ret);
|
|
- goto err;
|
|
+ goto error_fw_release;
|
|
}
|
|
}
|
|
|
|
ret = m88ds3103_wr_reg(priv, 0xb2, 0x00);
|
|
if (ret)
|
|
- goto err;
|
|
+ goto error_fw_release;
|
|
|
|
release_firmware(fw);
|
|
fw = NULL;
|
|
@@ -641,10 +686,10 @@
|
|
priv->warm = true;
|
|
|
|
return 0;
|
|
-err:
|
|
- if (fw)
|
|
- release_firmware(fw);
|
|
|
|
+error_fw_release:
|
|
+ release_firmware(fw);
|
|
+err:
|
|
dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
@@ -653,12 +698,18 @@
|
|
{
|
|
struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
int ret;
|
|
+ u8 u8tmp;
|
|
+
|
|
dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
|
|
|
|
priv->delivery_system = SYS_UNDEFINED;
|
|
|
|
/* TS Hi-Z */
|
|
- ret = m88ds3103_wr_reg_mask(priv, 0x27, 0x00, 0x01);
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID)
|
|
+ u8tmp = 0x29;
|
|
+ else
|
|
+ u8tmp = 0x27;
|
|
+ ret = m88ds3103_wr_reg_mask(priv, u8tmp, 0x00, 0x01);
|
|
if (ret)
|
|
goto err;
|
|
|
|
@@ -687,6 +738,7 @@
|
|
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
int ret;
|
|
u8 buf[3];
|
|
+
|
|
dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
|
|
|
|
if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
|
|
@@ -711,9 +763,6 @@
|
|
case 1:
|
|
c->inversion = INVERSION_ON;
|
|
break;
|
|
- default:
|
|
- dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n",
|
|
- __func__);
|
|
}
|
|
|
|
switch ((buf[1] >> 5) & 0x07) {
|
|
@@ -793,9 +842,6 @@
|
|
case 1:
|
|
c->pilot = PILOT_ON;
|
|
break;
|
|
- default:
|
|
- dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n",
|
|
- __func__);
|
|
}
|
|
|
|
switch ((buf[0] >> 6) & 0x07) {
|
|
@@ -823,9 +869,6 @@
|
|
case 1:
|
|
c->inversion = INVERSION_ON;
|
|
break;
|
|
- default:
|
|
- dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n",
|
|
- __func__);
|
|
}
|
|
|
|
switch ((buf[2] >> 0) & 0x03) {
|
|
@@ -855,7 +898,7 @@
|
|
goto err;
|
|
|
|
c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) *
|
|
- M88DS3103_MCLK_KHZ * 1000 / 0x10000;
|
|
+ priv->mclk_khz * 1000 / 0x10000;
|
|
|
|
return 0;
|
|
err:
|
|
@@ -871,6 +914,7 @@
|
|
u8 buf[3];
|
|
u16 noise, signal;
|
|
u32 noise_tot, signal_tot;
|
|
+
|
|
dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
|
|
/* reports SNR in resolution of 0.1 dB */
|
|
|
|
@@ -893,7 +937,7 @@
|
|
/* SNR(X) dB = 10 * ln(X) / ln(10) dB */
|
|
tmp = DIV_ROUND_CLOSEST(tmp, 8 * M88DS3103_SNR_ITERATIONS);
|
|
if (tmp)
|
|
- *snr = 100ul * intlog2(tmp) / intlog2(10);
|
|
+ *snr = div_u64((u64) 100 * intlog2(tmp), intlog2(10));
|
|
else
|
|
*snr = 0;
|
|
break;
|
|
@@ -922,7 +966,7 @@
|
|
/* SNR(X) dB = 10 * log10(X) dB */
|
|
if (signal > noise) {
|
|
tmp = signal / noise;
|
|
- *snr = 100ul * intlog10(tmp) / (1 << 24);
|
|
+ *snr = div_u64((u64) 100 * intlog10(tmp), (1 << 24));
|
|
} else {
|
|
*snr = 0;
|
|
}
|
|
@@ -940,6 +984,87 @@
|
|
return ret;
|
|
}
|
|
|
|
+static int m88ds3103_read_ber(struct dvb_frontend *fe, u32 *ber)
|
|
+{
|
|
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
+ int ret;
|
|
+ unsigned int utmp;
|
|
+ u8 buf[3], u8tmp;
|
|
+
|
|
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
|
|
+
|
|
+ switch (c->delivery_system) {
|
|
+ case SYS_DVBS:
|
|
+ ret = m88ds3103_wr_reg(priv, 0xf9, 0x04);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = m88ds3103_rd_reg(priv, 0xf8, &u8tmp);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ if (!(u8tmp & 0x10)) {
|
|
+ u8tmp |= 0x10;
|
|
+
|
|
+ ret = m88ds3103_rd_regs(priv, 0xf6, buf, 2);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ priv->ber = (buf[1] << 8) | (buf[0] << 0);
|
|
+
|
|
+ /* restart counters */
|
|
+ ret = m88ds3103_wr_reg(priv, 0xf8, u8tmp);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+ break;
|
|
+ case SYS_DVBS2:
|
|
+ ret = m88ds3103_rd_regs(priv, 0xd5, buf, 3);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ utmp = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
|
|
+
|
|
+ if (utmp > 3000) {
|
|
+ ret = m88ds3103_rd_regs(priv, 0xf7, buf, 2);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ priv->ber = (buf[1] << 8) | (buf[0] << 0);
|
|
+
|
|
+ /* restart counters */
|
|
+ ret = m88ds3103_wr_reg(priv, 0xd1, 0x01);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = m88ds3103_wr_reg(priv, 0xf9, 0x01);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = m88ds3103_wr_reg(priv, 0xf9, 0x00);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ ret = m88ds3103_wr_reg(priv, 0xd1, 0x00);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
|
|
+ __func__);
|
|
+ ret = -EINVAL;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ *ber = priv->ber;
|
|
+
|
|
+ return 0;
|
|
+err:
|
|
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
|
|
static int m88ds3103_set_tone(struct dvb_frontend *fe,
|
|
fe_sec_tone_mode_t fe_sec_tone_mode)
|
|
@@ -947,6 +1072,7 @@
|
|
struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
int ret;
|
|
u8 u8tmp, tone, reg_a1_mask;
|
|
+
|
|
dev_dbg(&priv->i2c->dev, "%s: fe_sec_tone_mode=%d\n", __func__,
|
|
fe_sec_tone_mode);
|
|
|
|
@@ -958,7 +1084,7 @@
|
|
switch (fe_sec_tone_mode) {
|
|
case SEC_TONE_ON:
|
|
tone = 0;
|
|
- reg_a1_mask = 0x87;
|
|
+ reg_a1_mask = 0x47;
|
|
break;
|
|
case SEC_TONE_OFF:
|
|
tone = 1;
|
|
@@ -987,12 +1113,64 @@
|
|
return ret;
|
|
}
|
|
|
|
+static int m88ds3103_set_voltage(struct dvb_frontend *fe,
|
|
+ fe_sec_voltage_t fe_sec_voltage)
|
|
+{
|
|
+ struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
+ int ret;
|
|
+ u8 u8tmp;
|
|
+ bool voltage_sel, voltage_dis;
|
|
+
|
|
+ dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__,
|
|
+ fe_sec_voltage);
|
|
+
|
|
+ if (!priv->warm) {
|
|
+ ret = -EAGAIN;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ switch (fe_sec_voltage) {
|
|
+ case SEC_VOLTAGE_18:
|
|
+ voltage_sel = true;
|
|
+ voltage_dis = false;
|
|
+ break;
|
|
+ case SEC_VOLTAGE_13:
|
|
+ voltage_sel = false;
|
|
+ voltage_dis = false;
|
|
+ break;
|
|
+ case SEC_VOLTAGE_OFF:
|
|
+ voltage_sel = false;
|
|
+ voltage_dis = true;
|
|
+ break;
|
|
+ default:
|
|
+ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n",
|
|
+ __func__);
|
|
+ ret = -EINVAL;
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ /* output pin polarity */
|
|
+ voltage_sel ^= priv->cfg->lnb_hv_pol;
|
|
+ voltage_dis ^= priv->cfg->lnb_en_pol;
|
|
+
|
|
+ u8tmp = voltage_dis << 1 | voltage_sel << 0;
|
|
+ ret = m88ds3103_wr_reg_mask(priv, 0xa2, u8tmp, 0x03);
|
|
+ if (ret)
|
|
+ goto err;
|
|
+
|
|
+ return 0;
|
|
+err:
|
|
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe,
|
|
struct dvb_diseqc_master_cmd *diseqc_cmd)
|
|
{
|
|
struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
int ret, i;
|
|
u8 u8tmp;
|
|
+
|
|
dev_dbg(&priv->i2c->dev, "%s: msg=%*ph\n", __func__,
|
|
diseqc_cmd->msg_len, diseqc_cmd->msg);
|
|
|
|
@@ -1064,6 +1242,7 @@
|
|
struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
int ret, i;
|
|
u8 u8tmp, burst;
|
|
+
|
|
dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__,
|
|
fe_sec_mini_cmd);
|
|
|
|
@@ -1136,6 +1315,7 @@
|
|
static void m88ds3103_release(struct dvb_frontend *fe)
|
|
{
|
|
struct m88ds3103_priv *priv = fe->demodulator_priv;
|
|
+
|
|
i2c_del_mux_adapter(priv->i2c_adapter);
|
|
kfree(priv);
|
|
}
|
|
@@ -1198,18 +1378,22 @@
|
|
priv->i2c = i2c;
|
|
mutex_init(&priv->i2c_mutex);
|
|
|
|
- ret = m88ds3103_rd_reg(priv, 0x01, &chip_id);
|
|
+ /* 0x00: chip id[6:0], 0x01: chip ver[7:0], 0x02: chip ver[15:8] */
|
|
+ ret = m88ds3103_rd_reg(priv, 0x00, &chip_id);
|
|
if (ret)
|
|
goto err;
|
|
|
|
- dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
|
|
+ chip_id >>= 1;
|
|
+ dev_info(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
|
|
|
|
switch (chip_id) {
|
|
- case 0xd0:
|
|
+ case M88RS6000_CHIP_ID:
|
|
+ case M88DS3103_CHIP_ID:
|
|
break;
|
|
default:
|
|
goto err;
|
|
}
|
|
+ priv->chip_id = chip_id;
|
|
|
|
switch (priv->cfg->clock_out) {
|
|
case M88DS3103_CLOCK_OUT_DISABLED:
|
|
@@ -1225,6 +1409,11 @@
|
|
goto err;
|
|
}
|
|
|
|
+ /* 0x29 register is defined differently for m88rs6000. */
|
|
+ /* set internal tuner address to 0x21 */
|
|
+ if (chip_id == M88RS6000_CHIP_ID)
|
|
+ u8tmp = 0x00;
|
|
+
|
|
ret = m88ds3103_wr_reg(priv, 0x29, u8tmp);
|
|
if (ret)
|
|
goto err;
|
|
@@ -1252,6 +1441,9 @@
|
|
|
|
/* create dvb_frontend */
|
|
memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
|
|
+ if (priv->chip_id == M88RS6000_CHIP_ID)
|
|
+ strncpy(priv->fe.ops.info.name,
|
|
+ "Montage M88RS6000", sizeof(priv->fe.ops.info.name));
|
|
priv->fe.demodulator_priv = priv;
|
|
|
|
return &priv->fe;
|
|
@@ -1298,14 +1490,17 @@
|
|
|
|
.read_status = m88ds3103_read_status,
|
|
.read_snr = m88ds3103_read_snr,
|
|
+ .read_ber = m88ds3103_read_ber,
|
|
|
|
.diseqc_send_master_cmd = m88ds3103_diseqc_send_master_cmd,
|
|
.diseqc_send_burst = m88ds3103_diseqc_send_burst,
|
|
|
|
.set_tone = m88ds3103_set_tone,
|
|
+ .set_voltage = m88ds3103_set_voltage,
|
|
};
|
|
|
|
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
|
|
MODULE_DESCRIPTION("Montage M88DS3103 DVB-S/S2 demodulator driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_FIRMWARE(M88DS3103_FIRMWARE);
|
|
+MODULE_FIRMWARE(M88RS6000_FIRMWARE);
|
|
--- a/drivers/media/dvb-frontends//m88ds3103_priv.h
|
|
+++ b/drivers/media/dvb-frontends//m88ds3103_priv.h
|
|
@@ -22,9 +22,13 @@
|
|
#include "dvb_math.h"
|
|
#include <linux/firmware.h>
|
|
#include <linux/i2c-mux.h>
|
|
+#include <linux/math64.h>
|
|
|
|
#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
|
|
+#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw"
|
|
#define M88DS3103_MCLK_KHZ 96000
|
|
+#define M88RS6000_CHIP_ID 0x74
|
|
+#define M88DS3103_CHIP_ID 0x70
|
|
|
|
struct m88ds3103_priv {
|
|
struct i2c_adapter *i2c;
|
|
@@ -34,8 +38,13 @@
|
|
struct dvb_frontend fe;
|
|
fe_delivery_system_t delivery_system;
|
|
fe_status_t fe_status;
|
|
+ u32 ber;
|
|
bool warm; /* FW running */
|
|
struct i2c_adapter *i2c_adapter;
|
|
+ /* auto detect chip id to do different config */
|
|
+ u8 chip_id;
|
|
+ /* main mclk is calculated for M88RS6000 dynamically */
|
|
+ u32 mclk_khz;
|
|
};
|
|
|
|
struct m88ds3103_reg_val {
|
|
@@ -212,4 +221,178 @@
|
|
{0xb8, 0x00},
|
|
};
|
|
|
|
+static const struct m88ds3103_reg_val m88rs6000_dvbs_init_reg_vals[] = {
|
|
+ {0x23, 0x07},
|
|
+ {0x08, 0x03},
|
|
+ {0x0c, 0x02},
|
|
+ {0x20, 0x00},
|
|
+ {0x21, 0x54},
|
|
+ {0x25, 0x82},
|
|
+ {0x27, 0x31},
|
|
+ {0x30, 0x08},
|
|
+ {0x31, 0x40},
|
|
+ {0x32, 0x32},
|
|
+ {0x33, 0x35},
|
|
+ {0x35, 0xff},
|
|
+ {0x3a, 0x00},
|
|
+ {0x37, 0x10},
|
|
+ {0x38, 0x10},
|
|
+ {0x39, 0x02},
|
|
+ {0x42, 0x60},
|
|
+ {0x4a, 0x80},
|
|
+ {0x4b, 0x04},
|
|
+ {0x4d, 0x91},
|
|
+ {0x5d, 0xc8},
|
|
+ {0x50, 0x36},
|
|
+ {0x51, 0x36},
|
|
+ {0x52, 0x36},
|
|
+ {0x53, 0x36},
|
|
+ {0x63, 0x0f},
|
|
+ {0x64, 0x30},
|
|
+ {0x65, 0x40},
|
|
+ {0x68, 0x26},
|
|
+ {0x69, 0x4c},
|
|
+ {0x70, 0x20},
|
|
+ {0x71, 0x70},
|
|
+ {0x72, 0x04},
|
|
+ {0x73, 0x00},
|
|
+ {0x70, 0x40},
|
|
+ {0x71, 0x70},
|
|
+ {0x72, 0x04},
|
|
+ {0x73, 0x00},
|
|
+ {0x70, 0x60},
|
|
+ {0x71, 0x70},
|
|
+ {0x72, 0x04},
|
|
+ {0x73, 0x00},
|
|
+ {0x70, 0x80},
|
|
+ {0x71, 0x70},
|
|
+ {0x72, 0x04},
|
|
+ {0x73, 0x00},
|
|
+ {0x70, 0xa0},
|
|
+ {0x71, 0x70},
|
|
+ {0x72, 0x04},
|
|
+ {0x73, 0x00},
|
|
+ {0x70, 0x1f},
|
|
+ {0x76, 0x38},
|
|
+ {0x77, 0xa6},
|
|
+ {0x78, 0x0c},
|
|
+ {0x79, 0x80},
|
|
+ {0x7f, 0x14},
|
|
+ {0x7c, 0x00},
|
|
+ {0xae, 0x82},
|
|
+ {0x80, 0x64},
|
|
+ {0x81, 0x66},
|
|
+ {0x82, 0x44},
|
|
+ {0x85, 0x04},
|
|
+ {0xcd, 0xf4},
|
|
+ {0x90, 0x33},
|
|
+ {0xa0, 0x44},
|
|
+ {0xbe, 0x00},
|
|
+ {0xc0, 0x08},
|
|
+ {0xc3, 0x10},
|
|
+ {0xc4, 0x08},
|
|
+ {0xc5, 0xf0},
|
|
+ {0xc6, 0xff},
|
|
+ {0xc7, 0x00},
|
|
+ {0xc8, 0x1a},
|
|
+ {0xc9, 0x80},
|
|
+ {0xe0, 0xf8},
|
|
+ {0xe6, 0x8b},
|
|
+ {0xd0, 0x40},
|
|
+ {0xf8, 0x20},
|
|
+ {0xfa, 0x0f},
|
|
+ {0x00, 0x00},
|
|
+ {0xbd, 0x01},
|
|
+ {0xb8, 0x00},
|
|
+ {0x29, 0x11},
|
|
+};
|
|
+
|
|
+static const struct m88ds3103_reg_val m88rs6000_dvbs2_init_reg_vals[] = {
|
|
+ {0x23, 0x07},
|
|
+ {0x08, 0x07},
|
|
+ {0x0c, 0x02},
|
|
+ {0x20, 0x00},
|
|
+ {0x21, 0x54},
|
|
+ {0x25, 0x82},
|
|
+ {0x27, 0x31},
|
|
+ {0x30, 0x08},
|
|
+ {0x32, 0x32},
|
|
+ {0x33, 0x35},
|
|
+ {0x35, 0xff},
|
|
+ {0x3a, 0x00},
|
|
+ {0x37, 0x10},
|
|
+ {0x38, 0x10},
|
|
+ {0x39, 0x02},
|
|
+ {0x42, 0x60},
|
|
+ {0x4a, 0x80},
|
|
+ {0x4b, 0x04},
|
|
+ {0x4d, 0x91},
|
|
+ {0x5d, 0xc8},
|
|
+ {0x50, 0x36},
|
|
+ {0x51, 0x36},
|
|
+ {0x52, 0x36},
|
|
+ {0x53, 0x36},
|
|
+ {0x63, 0x0f},
|
|
+ {0x64, 0x10},
|
|
+ {0x65, 0x20},
|
|
+ {0x68, 0x46},
|
|
+ {0x69, 0xcd},
|
|
+ {0x70, 0x20},
|
|
+ {0x71, 0x70},
|
|
+ {0x72, 0x04},
|
|
+ {0x73, 0x00},
|
|
+ {0x70, 0x40},
|
|
+ {0x71, 0x70},
|
|
+ {0x72, 0x04},
|
|
+ {0x73, 0x00},
|
|
+ {0x70, 0x60},
|
|
+ {0x71, 0x70},
|
|
+ {0x72, 0x04},
|
|
+ {0x73, 0x00},
|
|
+ {0x70, 0x80},
|
|
+ {0x71, 0x70},
|
|
+ {0x72, 0x04},
|
|
+ {0x73, 0x00},
|
|
+ {0x70, 0xa0},
|
|
+ {0x71, 0x70},
|
|
+ {0x72, 0x04},
|
|
+ {0x73, 0x00},
|
|
+ {0x70, 0x1f},
|
|
+ {0x76, 0x38},
|
|
+ {0x77, 0xa6},
|
|
+ {0x78, 0x0c},
|
|
+ {0x79, 0x80},
|
|
+ {0x7f, 0x14},
|
|
+ {0x85, 0x08},
|
|
+ {0xcd, 0xf4},
|
|
+ {0x90, 0x33},
|
|
+ {0x86, 0x00},
|
|
+ {0x87, 0x0f},
|
|
+ {0x89, 0x00},
|
|
+ {0x8b, 0x44},
|
|
+ {0x8c, 0x66},
|
|
+ {0x9d, 0xc1},
|
|
+ {0x8a, 0x10},
|
|
+ {0xad, 0x40},
|
|
+ {0xa0, 0x44},
|
|
+ {0xbe, 0x00},
|
|
+ {0xc0, 0x08},
|
|
+ {0xc1, 0x10},
|
|
+ {0xc2, 0x08},
|
|
+ {0xc3, 0x10},
|
|
+ {0xc4, 0x08},
|
|
+ {0xc5, 0xf0},
|
|
+ {0xc6, 0xff},
|
|
+ {0xc7, 0x00},
|
|
+ {0xc8, 0x1a},
|
|
+ {0xc9, 0x80},
|
|
+ {0xca, 0x23},
|
|
+ {0xcb, 0x24},
|
|
+ {0xcc, 0xf4},
|
|
+ {0xce, 0x74},
|
|
+ {0x00, 0x00},
|
|
+ {0xbd, 0x01},
|
|
+ {0xb8, 0x00},
|
|
+ {0x29, 0x01},
|
|
+};
|
|
#endif
|
|
--- a/drivers/media/dvb-core/dvb-usb-ids.h
|
|
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
|
|
@@ -244,6 +245,9 @@
|
|
#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006
|
|
#define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM 0x3009
|
|
#define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d
|
|
+#define USB_PID_TECHNOTREND_CONNECT_S2_4600 0x3011
|
|
+#define USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI 0x3012
|
|
+#define USB_PID_TECHNOTREND_TVSTICK_CT2_4400 0x3014
|
|
#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a
|
|
#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081
|
|
#define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058
|
|
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
|
|
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
|
|
@@ -814,7 +814,7 @@
|
|
.clock = 27000000,
|
|
.i2c_wr_max = 33,
|
|
.clock_out = 0,
|
|
- .ts_mode = M88DS3103_TS_PARALLEL_16,
|
|
+ .ts_mode = M88DS3103_TS_PARALLEL,
|
|
.agc = 0x99,
|
|
};
|
|
|