[PATCH 1/6 v2] dvbsky, montage dvb-s/s2 TS202x tuner and M88DS3103 demodulator driver
---
drivers/media/dvb/frontends/Kconfig | 14 +
drivers/media/dvb/frontends/Makefile | 3 +
drivers/media/dvb/frontends/m88ds3103.c | 1153 ++++++++++++++++++++++++++
drivers/media/dvb/frontends/m88ds3103.h | 67 ++
drivers/media/dvb/frontends/m88ds3103_priv.h | 413 +++++++++
drivers/media/dvb/frontends/m88ts202x.c | 590 +++++++++++++
drivers/media/dvb/frontends/m88ts202x.h | 63 ++
7 files changed, 2303 insertions(+)
create mode 100644 drivers/media/dvb/frontends/m88ds3103.c
create mode 100644 drivers/media/dvb/frontends/m88ds3103.h
create mode 100644 drivers/media/dvb/frontends/m88ds3103_priv.h
create mode 100644 drivers/media/dvb/frontends/m88ts202x.c
create mode 100644 drivers/media/dvb/frontends/m88ts202x.h
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index e11adb6..8d706f6 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -214,6 +214,20 @@ config DVB_CX24116
help
A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+config DVB_M88TS202X
+ tristate "Montage M88TS202X based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_M88DS3103
+ tristate "Montage M88DS3103 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
config DVB_SI21XX
tristate "Silicon Labs SI21XX based"
depends on DVB_CORE && I2C
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 6ca7557..1fdfcb4 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -98,5 +98,8 @@ obj-$(CONFIG_DVB_A8293) += a8293.o
obj-$(CONFIG_DVB_TDA10071) += tda10071.o
obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
+obj-$(CONFIG_DVB_M88DS3103) += m88ds3103.o
+obj-$(CONFIG_DVB_M88TS202X) += m88ts202x.o
obj-$(CONFIG_DVB_AF9033) += af9033.o
+
diff --git a/drivers/media/dvb/frontends/m88ds3103.c b/drivers/media/dvb/frontends/m88ds3103.c
new file mode 100644
index 0000000..392cada
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88ds3103.c
@@ -0,0 +1,1153 @@
+/*
+ Montage Technology M88DS3103/3000 - DVBS/S2 Satellite demod driver
+
+ Copyright (C) 2011 Max nibble<nibble.max@xxxxxxxxx>
+ Copyright (C) 2010 Montage Technology<www.montage-tech.com>
+ Fix some bug and add M88DS3103 code, M88DS3000 code based on DS3000.c.
+
+ Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@xxxxxxxxx>
+
+ Copyright (C) 2009 TurboSight.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 <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "m88ds3103.h"
+#include "m88ds3103_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+/*demod register operations.*/
+static int m88ds3103_writereg(struct m88ds3103_state *state, int reg, int data)
+{
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address,
+ .flags = 0, .buf = buf, .len = 2 };
+ int err;
+
+ if (debug > 1)
+ printk(KERN_INFO "m88ds3103: %s: write reg 0x%02x, value 0x%02x\n",
+ __func__, reg, data);
+
+ err = i2c_transfer(state->i2c, &msg, 1);
+ if (err != 1) {
+ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x,"
+ " value == 0x%02x)\n", __func__, err, reg, data);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int m88ds3103_readreg(struct m88ds3103_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ { .addr = state->config->demod_address, .flags = 0,
+ .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD,
+ .buf = b1, .len = 1 }
+ };
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk(KERN_ERR "%s: reg=0x%x (error=%d)\n",
+ __func__, reg, ret);
+ return ret;
+ }
+
+ if (debug > 1)
+ printk(KERN_INFO "m88ds3103: read reg 0x%02x, value 0x%02x\n",
+ reg, b1[0]);
+
+ return b1[0];
+}
+
+/* Bulk demod I2C write, for firmware download. */
+static int m88ds3103_writeregN(struct m88ds3103_state *state, int reg,
+ const u8 *data, u16 len)
+{
+ int ret = -EREMOTEIO;
+ struct i2c_msg msg;
+ u8 *buf;
+
+ buf = kmalloc(len + 1, GFP_KERNEL);
+ if (buf == NULL) {
+ printk(KERN_ERR "Unable to kmalloc\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ *(buf) = reg;
+ memcpy(buf + 1, data, len);
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.buf = buf;
+ msg.len = len + 1;
+
+ if (debug > 1)
+ printk(KERN_INFO "m88ds3103: %s: write regN 0x%02x, len = %d\n",
+ __func__, reg, len);
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+ if (ret != 1) {
+ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n",
+ __func__, ret, reg);
+ ret = -EREMOTEIO;
+ }
+error:
+ kfree(buf);
+
+ return ret;
+}
+
+static int m88ds3103_load_firmware(struct dvb_frontend *fe)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ const struct firmware *fw;
+ int i, ret = 0;
+
+ dprintk("%s()\n", __func__);
+
+ if (state->skip_fw_load)
+ return 0;
+ /* Load firmware */
+ /* request the firmware, this will block until someone uploads it */
+ if (state->demod_id == DS3000_ID) {
+ printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n",
+ __func__,
+ DS3000_DEFAULT_FIRMWARE);
+ ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE,
+ state->i2c->dev.parent);
+ } else if (state->demod_id == DS3103_ID) {
+ printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n",
+ __func__,
+ DS3103_DEFAULT_FIRMWARE);
+ ret = request_firmware(&fw, DS3103_DEFAULT_FIRMWARE,
+ state->i2c->dev.parent);
+ }
+
+ printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__);
+ if (ret) {
+ printk(KERN_ERR "%s: No firmware uploaded (timeout or file not "
+ "found?)\n", __func__);
+ return ret;
+ }
+
+ /* Make sure we don't recurse back through here during loading */
+ state->skip_fw_load = 1;
+
+ dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n",
+ fw->size,
+ fw->data[0],
+ fw->data[1],
+ fw->data[fw->size - 2],
+ fw->data[fw->size - 1]);
+ /* stop internal mcu. */
+ m88ds3103_writereg(state, 0xb2, 0x01);
+ /* split firmware to download.*/
+ for (i = 0; i < FW_DOWN_LOOP; i++) {
+ ret = m88ds3103_writeregN(state, 0xb0,
+ &(fw->data[FW_DOWN_SIZE*i]), FW_DOWN_SIZE);
+ if (ret != 1)
+ break;
+ }
+ /* start internal mcu. */
+ if (ret == 1)
+ m88ds3103_writereg(state, 0xb2, 0x00);
+
+ release_firmware(fw);
+
+ dprintk("%s: Firmware upload %s\n", __func__,
+ ret == 1 ? "complete" : "failed");
+
+ if (ret == 1)
+ ret = 0;
+ /* Ensure firmware is always loaded if required */
+ state->skip_fw_load = 0;
+
+ return ret;
+}
+
+
+static int m88ds3103_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ u8 data;
+
+ dprintk("%s(%d)\n", __func__, voltage);
+
+ dprintk("m88ds3103:pin_ctrl = (%02x)\n", state->config->pin_ctrl);
+
+ if (state->config->set_voltage)
+ state->config->set_voltage(fe, voltage);
+
+ data = m88ds3103_readreg(state, 0xa2);
+
+ if (state->config->pin_ctrl & 0x80) { /*If control pin is assigned.*/
+ data &= ~0x03; /* bit0 V/H, bit1 off/on */
+ if (state->config->pin_ctrl & 0x02)
+ data |= 0x02;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_18:
+ if ((state->config->pin_ctrl & 0x01) == 0)
+ data |= 0x01;
+ break;
+ case SEC_VOLTAGE_13:
+ if ((state->config->pin_ctrl) & 0x01)
+ data |= 0x01;
+ break;
+ case SEC_VOLTAGE_OFF:
+ if ((state->config->pin_ctrl) & 0x02)
+ data &= ~0x02;
+ else
+ data |= 0x02;
+ break;
+ }
+ }
+ m88ds3103_writereg(state, 0xa2, data);
+
+ return 0;
+}
+
+static int m88ds3103_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ int lock = 0;
+
+ *status = 0;
+
+ switch (state->delivery_system) {
+ case SYS_DVBS:
+ lock = m88ds3103_readreg(state, 0xd1);
+ dprintk("%s: SYS_DVBS status=%x.\n", __func__, lock);
+
+ if ((lock & 0x07) == 0x07) {
+ /*if((m88ds3103_readreg(state, 0x0d) & 0x07) == 0x07)*/
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER
+ | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ }
+ break;
+ case SYS_DVBS2:
+ lock = m88ds3103_readreg(state, 0x0d);
+ dprintk("%s: SYS_DVBS2 status=%x.\n", __func__, lock);
+
+ if ((lock & 0x8f) == 0x8f)
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER
+ | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int m88ds3103_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ u8 tmp1, tmp2, tmp3;
+ u32 ldpc_frame_cnt, pre_err_packags, code_rate_fac = 0;
+
+ dprintk("%s()\n", __func__);
+
+ switch (state->delivery_system) {
+ case SYS_DVBS:
+ m88ds3103_writereg(state, 0xf9, 0x04);
+ tmp3 = m88ds3103_readreg(state, 0xf8);
+ if ((tmp3 & 0x10) == 0) {
+ tmp1 = m88ds3103_readreg(state, 0xf7);
+ tmp2 = m88ds3103_readreg(state, 0xf6);
+ tmp3 |= 0x10;
+ m88ds3103_writereg(state, 0xf8, tmp3);
+ state->preBer = (tmp1 << 8) | tmp2;
+ }
+ break;
+ case SYS_DVBS2:
+ tmp1 = m88ds3103_readreg(state, 0x7e) & 0x0f;
+ switch (tmp1) {
+ case 0:
+ code_rate_fac = 16008 - 80;
+ break;
+ case 1:
+ code_rate_fac = 21408 - 80;
+ break;
+ case 2:
+ code_rate_fac = 25728 - 80;
+ break;
+ case 3:
+ code_rate_fac = 32208 - 80;
+ break;
+ case 4:
+ code_rate_fac = 38688 - 80;
+ break;
+ case 5:
+ code_rate_fac = 43040 - 80;
+ break;
+ case 6:
+ code_rate_fac = 48408 - 80;
+ break;
+ case 7:
+ code_rate_fac = 51648 - 80;
+ break;
+ case 8:
+ code_rate_fac = 53840 - 80;
+ break;
+ case 9:
+ code_rate_fac = 57472 - 80;
+ break;
+ case 10:
+ code_rate_fac = 58192 - 80;
+ break;
+ }
+
+ tmp1 = m88ds3103_readreg(state, 0xd7) & 0xff;
+ tmp2 = m88ds3103_readreg(state, 0xd6) & 0xff;
+ tmp3 = m88ds3103_readreg(state, 0xd5) & 0xff;
+ ldpc_frame_cnt = (tmp1 << 16) | (tmp2 << 8) | tmp3;
+
+ tmp1 = m88ds3103_readreg(state, 0xf8) & 0xff;
+ tmp2 = m88ds3103_readreg(state, 0xf7) & 0xff;
+ pre_err_packags = tmp1<<8 | tmp2;
+ if (ldpc_frame_cnt > 1000) {
+ m88ds3103_writereg(state, 0xd1, 0x01);
+ m88ds3103_writereg(state, 0xf9, 0x01);
+ m88ds3103_writereg(state, 0xf9, 0x00);
+ m88ds3103_writereg(state, 0xd1, 0x00);
+ state->preBer = pre_err_packags;
+ }
+ break;
+ default:
+ break;
+ }
+ *ber = state->preBer;
+
+ return 0;
+}
+
+static int m88ds3103_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ u16 gain = 1000;
+
+ dprintk("%s()\n", __func__);
+
+ if (state->config->tuner_get_rfgain)
+ state->config->tuner_get_rfgain(fe, &gain);
+
+ *signal_strength = 60000 - gain*55;
+
+ return 0;
+}
+
+
+static int m88ds3103_read_snr(struct dvb_frontend *fe, u16 *p_snr)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ u8 val, npow1, npow2, spow1, cnt;
+ u16 tmp, snr;
+ u32 npow, spow, snr_total;
+ static const u16 mes_log10[] = {
+ 0, 3010, 4771, 6021, 6990, 7781, 8451, 9031,
+ 9542, 10000, 10414, 10792, 11139, 11461, 11761, 12041,
+ 12304, 12553, 12788, 13010, 13222, 13424, 13617, 13802,
+ 13979, 14150, 14314, 14472, 14624, 14771, 14914, 15052,
+ 15185, 15315, 15441, 15563, 15682, 15798, 15911, 16021,
+ 16128, 16232, 16335, 16435, 16532, 16628, 16721, 16812,
+ 16902, 16990, 17076, 17160, 17243, 17324, 17404, 17482,
+ 17559, 17634, 17709, 17782, 17853, 17924, 17993, 18062,
+ 18129, 18195, 18261, 18325, 18388, 18451, 18513, 18573,
+ 18633, 18692, 18751, 18808, 18865, 18921, 18976, 19031
+ };
+ static const u16 mes_loge[] = {
+ 0, 6931, 10986, 13863, 16094, 17918, 19459, 20794,
+ 21972, 23026, 23979, 24849, 25649, 26391, 27081, 27726,
+ 28332, 28904, 29444, 29957, 30445, 30910, 31355, 31781,
+ 32189, 32581, 32958, 33322, 33673, 34012, 34340, 34657,
+ };
+
+ dprintk("%s()\n", __func__);
+
+ snr = 0;
+
+ switch (state->delivery_system) {
+ case SYS_DVBS:
+ cnt = 10; snr_total = 0;
+ while (cnt > 0) {
+ val = m88ds3103_readreg(state, 0xff);
+ snr_total += val;
+ cnt--;
+ }
+ tmp = (u16)(snr_total/80);
+ if (tmp > 0) {
+ if (tmp > 32)
+ tmp = 32;
+ snr = (mes_loge[tmp - 1] * 100) / 45;
+ } else {
+ snr = 0;
+ }
+ break;
+ case SYS_DVBS2:
+ cnt = 10; npow = 0; spow = 0;
+ while (cnt > 0) {
+ npow1 = m88ds3103_readreg(state, 0x8c) & 0xff;
+ npow2 = m88ds3103_readreg(state, 0x8d) & 0xff;
+ npow += (((npow1 & 0x3f) + (u16)(npow2 << 6)) >> 2);
+
+ spow1 = m88ds3103_readreg(state, 0x8e) & 0xff;
+ spow += ((spow1 * spow1) >> 1);
+ cnt--;
+ }
+ npow /= 10; spow /= 10;
+ if (spow == 0) {
+ snr = 0;
+ } else if (npow == 0) {
+ snr = 19;
+ } else {
+ if (spow > npow) {
+ tmp = (u16)(spow / npow);
+ if (tmp > 80)
+ tmp = 80;
+ snr = mes_log10[tmp - 1]*3;
+ } else {
+ tmp = (u16)(npow / spow);
+ if (tmp > 80)
+ tmp = 80;
+ snr = -(mes_log10[tmp - 1] / 1000);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ *p_snr = snr;
+
+ return 0;
+}
+
+
+static int m88ds3103_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ u8 tmp1, tmp2, tmp3, data;
+
+ dprintk("%s()\n", __func__);
+
+ switch (state->delivery_system) {
+ case SYS_DVBS:
+ data = m88ds3103_readreg(state, 0xf8);
+ data |= 0x40;
+ m88ds3103_writereg(state, 0xf8, data);
+ tmp1 = m88ds3103_readreg(state, 0xf5);
+ tmp2 = m88ds3103_readreg(state, 0xf4);
+ *ucblocks = (tmp1 << 8) | tmp2;
+ data &= ~0x20;
+ m88ds3103_writereg(state, 0xf8, data);
+ data |= 0x20;
+ m88ds3103_writereg(state, 0xf8, data);
+ data &= ~0x40;
+ m88ds3103_writereg(state, 0xf8, data);
+ break;
+ case SYS_DVBS2:
+ tmp1 = m88ds3103_readreg(state, 0xda);
+ tmp2 = m88ds3103_readreg(state, 0xd9);
+ tmp3 = m88ds3103_readreg(state, 0xd8);
+ *ucblocks = (tmp1 << 16)|(tmp2 << 8)|tmp3;
+ data = m88ds3103_readreg(state, 0xd1);
+ data |= 0x01;
+ m88ds3103_writereg(state, 0xd1, data);
+ data &= ~0x01;
+ m88ds3103_writereg(state, 0xd1, data);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int m88ds3103_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ u8 data_a1, data_a2;
+
+ dprintk("%s(%d)\n", __func__, tone);
+ if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
+ printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone);
+ return -EINVAL;
+ }
+
+ data_a1 = m88ds3103_readreg(state, 0xa1);
+ data_a2 = m88ds3103_readreg(state, 0xa2);
+ if (state->demod_id == DS3103_ID)
+ data_a2 &= 0xdf; /* Normal mode */
+ switch (tone) {
+ case SEC_TONE_ON:
+ dprintk("%s: SEC_TONE_ON\n", __func__);
+ data_a1 |= 0x04;
+ data_a1 &= ~0x03;
+ data_a1 &= ~0x40;
+ data_a2 &= ~0xc0;
+ break;
+ case SEC_TONE_OFF:
+ dprintk("%s: SEC_TONE_OFF\n", __func__);
+ data_a2 &= ~0xc0;
+ data_a2 |= 0x80;
+ break;
+ }
+ m88ds3103_writereg(state, 0xa2, data_a2);
+ m88ds3103_writereg(state, 0xa1, data_a1);
+ return 0;
+}
+
+static int m88ds3103_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *d)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ int i, ret = 0;
+ u8 tmp, time_out;
+
+ /* Dump DiSEqC message */
+ if (debug) {
+ printk(KERN_INFO "m88ds3103: %s(", __func__);
+ for (i = 0 ; i < d->msg_len ;) {
+ printk(KERN_INFO "0x%02x", d->msg[i]);
+ if (++i < d->msg_len)
+ printk(KERN_INFO ", ");
+ }
+ }
+
+ tmp = m88ds3103_readreg(state, 0xa2);
+ tmp &= ~0xc0;
+ if (state->demod_id == DS3103_ID)
+ tmp &= ~0x20;
+ m88ds3103_writereg(state, 0xa2, tmp);
+
+ for (i = 0; i < d->msg_len; i++)
+ m88ds3103_writereg(state, (0xa3+i), d->msg[i]);
+
+ tmp = m88ds3103_readreg(state, 0xa1);
+ tmp &= ~0x38;
+ tmp &= ~0x40;
+ tmp |= ((d->msg_len-1) << 3) | 0x07;
+ tmp &= ~0x80;
+ m88ds3103_writereg(state, 0xa1, tmp);
+ /* 1.5 * 9 * 8 = 108ms */
+ time_out = 150;
+ while (time_out > 0) {
+ msleep(10);
+ time_out -= 10;
+ tmp = m88ds3103_readreg(state, 0xa1);
+ if ((tmp & 0x40) == 0)
+ break;
+ }
+ if (time_out == 0) {
+ tmp = m88ds3103_readreg(state, 0xa1);
+ tmp &= ~0x80;
+ tmp |= 0x40;
+ m88ds3103_writereg(state, 0xa1, tmp);
+ ret = 1;
+ }
+ tmp = m88ds3103_readreg(state, 0xa2);
+ tmp &= ~0xc0;
+ tmp |= 0x80;
+ m88ds3103_writereg(state, 0xa2, tmp);
+ return ret;
+}
+
+
+static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t burst)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ u8 val, time_out;
+
+ dprintk("%s()\n", __func__);
+
+ val = m88ds3103_readreg(state, 0xa2);
+ val &= ~0xc0;
+ if (state->demod_id == DS3103_ID)
+ val &= 0xdf; /* Normal mode */
+ m88ds3103_writereg(state, 0xa2, val);
+ /* DiSEqC burst */
+ if (burst == SEC_MINI_B)
+ m88ds3103_writereg(state, 0xa1, 0x01);
+ else
+ m88ds3103_writereg(state, 0xa1, 0x02);
+
+ msleep(13);
+
+ time_out = 5;
+ do {
+ val = m88ds3103_readreg(state, 0xa1);
+ if ((val & 0x40) == 0)
+ break;
+ msleep(1);
+ time_out--;
+ } while (time_out > 0);
+
+ val = m88ds3103_readreg(state, 0xa2);
+ val &= ~0xc0;
+ val |= 0x80;
+ m88ds3103_writereg(state, 0xa2, val);
+
+ return 0;
+}
+
+static void m88ds3103_release(struct dvb_frontend *fe)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+ kfree(state);
+}
+
+static int m88ds3103_check_id(struct m88ds3103_state *state)
+{
+ int val_01;
+
+ /*check demod id*/
+ val_01 = m88ds3103_readreg(state, 0x01);
+ printk(KERN_INFO "m88ds3103 chip version: %x found\n", val_01);
+
+ if (val_01 == 0xD0)
+ state->demod_id = DS3103_ID;
+ else if (val_01 == 0xC0)
+ state->demod_id = DS3000_ID;
+ else
+ state->demod_id = UNKNOW_ID;
+
+ return state->demod_id;
+}
+
+static int m88ds3103_set_carrier_offset(struct dvb_frontend *fe,
+ s32 carrier_offset_khz)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ s32 tmp;
+
+ tmp = carrier_offset_khz;
+ tmp *= 65536;
+ tmp = (2*tmp + MT_FE_MCLK_KHZ) / (2*MT_FE_MCLK_KHZ);
+
+ if (tmp < 0)
+ tmp += 65536;
+
+ m88ds3103_writereg(state, 0x5f, tmp >> 8);
+ m88ds3103_writereg(state, 0x5e, tmp & 0xff);
+
+ return 0;
+}
+
+static int m88ds3103_set_symrate(struct dvb_frontend *fe)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u16 value;
+
+ value = (((c->symbol_rate / 1000) << 15)
+ + (MT_FE_MCLK_KHZ / 4)) / (MT_FE_MCLK_KHZ / 2);
+ m88ds3103_writereg(state, 0x61, value & 0x00ff);
+ m88ds3103_writereg(state, 0x62, (value & 0xff00) >> 8);
+
+ return 0;
+}
+
+static int m88ds3103_set_CCI(struct dvb_frontend *fe)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ u8 tmp;
+
+ tmp = m88ds3103_readreg(state, 0x56);
+ tmp &= ~0x01;
+ m88ds3103_writereg(state, 0x56, tmp);
+
+ tmp = m88ds3103_readreg(state, 0x76);
+ tmp &= ~0x80;
+ m88ds3103_writereg(state, 0x76, tmp);
+
+ return 0;
+}
+
+static int m88ds3103_init_reg(struct m88ds3103_state *state,
+ const u8 *p_reg_tab,
+ u32 size)
+{
+ u32 i;
+
+ for (i = 0; i < size; i += 2)
+ m88ds3103_writereg(state, p_reg_tab[i], p_reg_tab[i+1]);
+
+ return 0;
+}
+
+static int m88ds3103_demod_connect(struct dvb_frontend *fe,
+ s32 carrier_offset_khz)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u16 value;
+ u8 val1, val2, data;
+
+ dprintk("connect delivery system = %d\n", state->delivery_system);
+
+ /* ds3000 global reset */
+ m88ds3103_writereg(state, 0x07, 0x80);
+ m88ds3103_writereg(state, 0x07, 0x00);
+ /* ds3000 build-in uC reset */
+ m88ds3103_writereg(state, 0xb2, 0x01);
+ /* ds3000 software reset */
+ m88ds3103_writereg(state, 0x00, 0x01);
+
+ switch (state->delivery_system) {
+ case SYS_DVBS:
+ /* initialise the demod in DVB-S mode */
+ if (state->demod_id == DS3000_ID) {
+ m88ds3103_init_reg(state, ds3000_dvbs_init_tab,
+ sizeof(ds3000_dvbs_init_tab));
+
+ value = m88ds3103_readreg(state, 0xfe);
+ value &= 0xc0;
+ value |= 0x1b;
+ m88ds3103_writereg(state, 0xfe, value);
+
+ if (state->config->ci_mode)
+ val1 = 0x80;
+ else if (state->config->ts_mode)
+ val1 = 0x60;
+ else
+ val1 = 0x20;
+ m88ds3103_writereg(state, 0xfd, val1);
+
+ } else if (state->demod_id == DS3103_ID) {
+ m88ds3103_init_reg(state, ds3103_dvbs_init_tab,
+ sizeof(ds3103_dvbs_init_tab));
+
+ /* set ts clock */
+ if (state->config->ts_mode == 0) {
+ val1 = 3; val2 = 3;
+ } else {
+ val1 = 0; val2 = 0;
+ }
+ val1 -= 1; val2 -= 1;
+ val1 &= 0x3f; val2 &= 0x3f;
+ data = m88ds3103_readreg(state, 0xfe);
+ data &= 0xf0;
+ data |= (val2 >> 2) & 0x0f;
+ m88ds3103_writereg(state, 0xfe, data);
+ data = (val2 & 0x03) << 6;
+ data |= val1;
+ m88ds3103_writereg(state, 0xea, data);
+
+ m88ds3103_writereg(state, 0x4d,
+ 0xfd & m88ds3103_readreg(state, 0x4d));
+ m88ds3103_writereg(state, 0x30,
+ 0xef & m88ds3103_readreg(state, 0x30));
+
+ /* set master clock */
+ val1 = m88ds3103_readreg(state, 0x22);
+ val2 = m88ds3103_readreg(state, 0x24);
+ val1 &= 0x3f;
+ val2 &= 0x3f;
+ val1 |= 0x80;
+ val2 |= 0x40;
+
+ m88ds3103_writereg(state, 0x22, val1);
+ m88ds3103_writereg(state, 0x24, val2);
+
+ if (state->config->ci_mode)
+ val1 = 0x03;
+ else if (state->config->ts_mode)
+ val1 = 0x06;
+ else
+ val1 = 0x42;
+ m88ds3103_writereg(state, 0xfd, val1);
+ }
+ break;
+ case SYS_DVBS2:
+ /* initialise the demod in DVB-S2 mode */
+ if (state->demod_id == DS3000_ID) {
+ m88ds3103_init_reg(state, ds3000_dvbs2_init_tab,
+ sizeof(ds3000_dvbs2_init_tab));
+
+ if (c->symbol_rate >= 30000000)
+ m88ds3103_writereg(state, 0xfe, 0x54);
+ else
+ m88ds3103_writereg(state, 0xfe, 0x98);
+
+ } else if (state->demod_id == DS3103_ID) {
+ m88ds3103_init_reg(state, ds3103_dvbs2_init_tab,
+ sizeof(ds3103_dvbs2_init_tab));
+
+ /* set ts clock */
+ if (state->config->ts_mode == 0) {
+ val1 = 5; val2 = 4;
+ } else {
+ val1 = 0; val2 = 0;
+ }
+ val1 -= 1; val2 -= 1;
+ val1 &= 0x3f; val2 &= 0x3f;
+ data = m88ds3103_readreg(state, 0xfe);
+ data &= 0xf0;
+ data |= (val2 >> 2) & 0x0f;
+ m88ds3103_writereg(state, 0xfe, data);
+ data = (val2 & 0x03) << 6;
+ data |= val1;
+ m88ds3103_writereg(state, 0xea, data);
+
+ m88ds3103_writereg(state, 0x4d,
+ 0xfd & m88ds3103_readreg(state, 0x4d));
+ m88ds3103_writereg(state, 0x30,
+ 0xef & m88ds3103_readreg(state, 0x30));
+
+ /* set master clock */
+ val1 = m88ds3103_readreg(state, 0x22);
+ val2 = m88ds3103_readreg(state, 0x24);
+ val1 &= 0x3f;
+ val2 &= 0x3f;
+ if (state->config->ts_mode == 1) {
+ val1 |= 0x80;
+ val2 |= 0x40;
+ } else {
+ if (c->symbol_rate >= 28000000) {
+ val1 |= 0xc0;
+ } else if (c->symbol_rate >= 18000000) {
+ val2 |= 0x40;
+ } else {
+ val1 |= 0x80;
+ val2 |= 0x40;
+ }
+ }
+ m88ds3103_writereg(state, 0x22, val1);
+ m88ds3103_writereg(state, 0x24, val2);
+ }
+
+ if (state->config->ci_mode)
+ val1 = 0x03;
+ else if (state->config->ts_mode)
+ val1 = 0x06;
+ else
+ val1 = 0x42;
+ m88ds3103_writereg(state, 0xfd, val1);
+
+ break;
+ default:
+ return 1;
+ }
+ /* disable 27MHz clock output */
+ m88ds3103_writereg(state, 0x29, 0x80);
+ /* enable ac coupling */
+ m88ds3103_writereg(state, 0x25, 0x8a);
+
+ if ((c->symbol_rate / 1000) <= 3000) {
+ /* 8 * 32 * 100 / 64 = 400*/
+ m88ds3103_writereg(state, 0xc3, 0x08);
+ m88ds3103_writereg(state, 0xc8, 0x20);
+ /* 8 * 0 * 100 / 128 = 0*/
+ m88ds3103_writereg(state, 0xc4, 0x08);
+ m88ds3103_writereg(state, 0xc7, 0x00);
+ } else if ((c->symbol_rate / 1000) <= 10000) {
+ /* 8 * 16 * 100 / 64 = 200*/
+ m88ds3103_writereg(state, 0xc3, 0x08);
+ m88ds3103_writereg(state, 0xc8, 0x10);
+ /* 8 * 0 * 100 / 128 = 0*/
+ m88ds3103_writereg(state, 0xc4, 0x08);
+ m88ds3103_writereg(state, 0xc7, 0x00);
+ } else {
+ /* 8 * 6 * 100 / 64 = 75*/
+ m88ds3103_writereg(state, 0xc3, 0x08);
+ m88ds3103_writereg(state, 0xc8, 0x06);
+ /* 8 * 0 * 100 / 128 = 0*/
+ m88ds3103_writereg(state, 0xc4, 0x08);
+ m88ds3103_writereg(state, 0xc7, 0x00);
+ }
+
+ m88ds3103_set_symrate(fe);
+ m88ds3103_set_CCI(fe);
+ m88ds3103_set_carrier_offset(fe, carrier_offset_khz);
+
+ /* ds3000 out of software reset */
+ m88ds3103_writereg(state, 0x00, 0x00);
+ /* start ds3000 build-in uC */
+ m88ds3103_writereg(state, 0xb2, 0x00);
+
+ return 0;
+}
+
+static int m88ds3103_set_frontend(struct dvb_frontend *fe)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ int i;
+ fe_status_t status;
+ s32 offset_khz;
+
+ dprintk("%s() ", __func__);
+ dprintk("c frequency = %d\n", c->frequency);
+ dprintk("symbol rate = %d\n", c->symbol_rate);
+ dprintk("delivery system = %d\n", c->delivery_system);
+
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
+
+ if (state->config->tuner_set_frequency)
+ state->config->tuner_set_frequency(fe, &offset_khz);
+
+ m88ds3103_demod_connect(fe, offset_khz);
+
+ for (i = 0; i < 30 ; i++) {
+ m88ds3103_read_status(fe, &status);
+ if (status & FE_HAS_LOCK)
+ break;
+ msleep(20);
+ }
+
+ if ((status & FE_HAS_LOCK) == 0) {
+ state->delivery_system = (state->delivery_system == SYS_DVBS)
+ ? SYS_DVBS2 : SYS_DVBS;
+ m88ds3103_demod_connect(fe, offset_khz);
+
+ for (i = 0; i < 30 ; i++) {
+ m88ds3103_read_status(fe, &status);
+ if (status & FE_HAS_LOCK)
+ break;
+ msleep(20);
+ }
+ }
+
+ if (status & FE_HAS_LOCK) {
+ if (state->config->start_ctrl) {
+ if (state->first_lock == 0) {
+ state->config->start_ctrl(fe);
+ state->first_lock = 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int m88ds3103_tune(struct dvb_frontend *fe,
+ bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ fe_status_t *status)
+{
+ *delay = HZ / 5;
+
+ dprintk("%s() ", __func__);
+ dprintk("re_tune = %d\n", re_tune);
+
+ if (re_tune) {
+ int ret = m88ds3103_set_frontend(fe);
+ if (ret)
+ return ret;
+ }
+
+ return m88ds3103_read_status(fe, status);
+}
+
+static enum dvbfe_algo m88ds3103_get_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+ /*
+ * Power config will reset and load initial firmware if required
+ */
+static int m88ds3103_initilaze(struct dvb_frontend *fe)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+ int ret;
+
+ dprintk("%s()\n", __func__);
+ /* hard reset */
+ m88ds3103_writereg(state, 0x07, 0x80);
+ m88ds3103_writereg(state, 0x07, 0x00);
+ msleep(1);
+
+ m88ds3103_writereg(state, 0x08, 0x01 | m88ds3103_readreg(state, 0x08));
+ msleep(1);
+
+ if (state->config->tuner_init)
+ state->config->tuner_init(fe);
+
+ if (state->demod_id == DS3103_ID) {
+ m88ds3103_writereg(state, 0x07, 0xe0);
+ m88ds3103_writereg(state, 0x07, 0x00);
+ msleep(1);
+ }
+ m88ds3103_writereg(state, 0xb2, 0x01);
+
+ /* Load the firmware if required */
+ ret = m88ds3103_load_firmware(fe);
+ if (ret != 0) {
+ printk(KERN_ERR "%s: Unable initialize firmware\n", __func__);
+ return ret;
+ }
+ if (state->demod_id == DS3103_ID) {
+ m88ds3103_writereg(state, 0x4d,
+ 0xfd & m88ds3103_readreg(state, 0x4d));
+ m88ds3103_writereg(state, 0x30,
+ 0xef & m88ds3103_readreg(state, 0x30));
+ }
+
+ return 0;
+}
+
+/*
+ * Initialise or wake up device
+ */
+static int m88ds3103_initfe(struct dvb_frontend *fe)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+
+ dprintk("%s()\n", __func__);
+
+ /* 1st step to wake up demod */
+ m88ds3103_writereg(state, 0x08, 0x01 | m88ds3103_readreg(state, 0x08));
+ m88ds3103_writereg(state, 0x04, 0xfe & m88ds3103_readreg(state, 0x04));
+ m88ds3103_writereg(state, 0x23, 0xef & m88ds3103_readreg(state, 0x23));
+ /* 2nd step to wake up tuner */
+ if (state->config->tuner_wakeup)
+ state->config->tuner_wakeup(fe);
+
+ return 0;
+}
+
+/* Put device to sleep */
+static int m88ds3103_sleep(struct dvb_frontend *fe)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+
+ dprintk("%s()\n", __func__);
+
+ /* 1st step to sleep tuner */
+ if (state->config->tuner_sleep)
+ state->config->tuner_sleep(fe);
+
+ /* 2nd step to sleep demod */
+ m88ds3103_writereg(state, 0x08, 0xfe & m88ds3103_readreg(state, 0x08));
+ m88ds3103_writereg(state, 0x04, 0x01 | m88ds3103_readreg(state, 0x04));
+ m88ds3103_writereg(state, 0x23, 0x10 | m88ds3103_readreg(state, 0x23));
+ return 0;
+
+}
+
+/* i2c gate control */
+static int m88ds3103_i2c_gate(struct dvb_frontend *fe, int enable)
+{
+ struct m88ds3103_state *state = fe->demodulator_priv;
+
+ m88ds3103_writereg(state, 0x03, 0x11);
+
+ return 0;
+}
+
+static struct dvb_frontend_ops m88ds3103_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2},
+ .info = {
+ .name = "Montage M88DS3103",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1011, /* kHz for QPSK frontends */
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_QPSK | FE_CAN_RECOVER
+ },
+
+ .release = m88ds3103_release,
+
+ .init = m88ds3103_initfe,
+ .sleep = m88ds3103_sleep,
+ .read_status = m88ds3103_read_status,
+ .read_ber = m88ds3103_read_ber,
+ .read_signal_strength = m88ds3103_read_signal_strength,
+ .read_snr = m88ds3103_read_snr,
+ .read_ucblocks = m88ds3103_read_ucblocks,
+ .set_tone = m88ds3103_set_tone,
+ .set_voltage = m88ds3103_set_voltage,
+ .diseqc_send_master_cmd = m88ds3103_send_diseqc_msg,
+ .diseqc_send_burst = m88ds3103_diseqc_send_burst,
+ .get_frontend_algo = m88ds3103_get_algo,
+ .tune = m88ds3103_tune,
+ .set_frontend = m88ds3103_set_frontend,
+ .i2c_gate_ctrl = m88ds3103_i2c_gate,
+};
+
+struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct m88ds3103_state *state = NULL;
+
+ dprintk("%s\n", __func__);
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct m88ds3103_state), GFP_KERNEL);
+ if (state == NULL) {
+ printk(KERN_ERR "Unable to kmalloc\n");
+ goto error2;
+ }
+
+ state->config = config;
+ state->i2c = i2c;
+ state->preBer = 0xffff;
+ state->delivery_system = SYS_DVBS; /*Default to DVB-S.*/
+
+ /* check demod id */
+ if (m88ds3103_check_id(state) == UNKNOW_ID) {
+ printk(KERN_ERR "Unable to find Montage demod chip\n");
+ goto error3;
+ }
+
+ memcpy(&state->frontend.ops, &m88ds3103_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ m88ds3103_initilaze(&state->frontend);
+
+ return &state->frontend;
+
+error3:
+ kfree(state);
+error2:
+ return NULL;
+}
+EXPORT_SYMBOL(m88ds3103_attach);
+
+MODULE_DESCRIPTION("DVB demod module for Montage M88DS3103");
+MODULE_AUTHOR("Max nibble");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/m88ds3103.h b/drivers/media/dvb/frontends/m88ds3103.h
new file mode 100644
index 0000000..ba6e637
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88ds3103.h
@@ -0,0 +1,67 @@
+/*
+ Montage Technology M88DS3103/3000 - DVBS/S2 Satellite demod driver
+
+ Copyright (C) 2011 Max nibble<nibble.max@xxxxxxxxx>
+ Copyright (C) 2010 Montage Technology<www.montage-tech.com>
+ Fix some bug and add M88DS3103 code, M88DS3000 code based on DS3000.c.
+
+ Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@xxxxxxxxx>
+
+ Copyright (C) 2009 TurboSight.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.
+*/
+
+
+#ifndef _M88DS3103_H
+#define _M88DS3103_H
+
+#include <linux/dvb/frontend.h>
+
+struct m88ds3103_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+ u8 ci_mode;
+ u8 pin_ctrl;
+ u8 ts_mode; /* 0: Parallel, 1: Serial */
+ /* Set device param to start dma */
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+ /* Start to transfer data */
+ int (*start_ctrl)(struct dvb_frontend *fe);
+ /* Set LNB voltage */
+ int (*set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+ /* tuner control */
+ int (*tuner_init) (struct dvb_frontend *fe);
+ int (*tuner_sleep) (struct dvb_frontend *fe);
+ int (*tuner_wakeup) (struct dvb_frontend *fe);
+ int (*tuner_set_frequency) (struct dvb_frontend *fe, s32 *pfreqOffset);
+ int (*tuner_get_rfgain) (struct dvb_frontend *fe, u16 *prfgain);
+};
+
+#if defined(CONFIG_DVB_M88DS3103) || \
+ (defined(CONFIG_DVB_M88DS3103_MODULE) && defined(MODULE))
+extern struct dvb_frontend *m88ds3103_attach(
+ const struct m88ds3103_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *m88ds3103_attach(
+ const struct m88ds3103_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_M88DS3103 */
+#endif /* M88DS3103_H */
diff --git a/drivers/media/dvb/frontends/m88ds3103_priv.h b/drivers/media/dvb/frontends/m88ds3103_priv.h
new file mode 100644
index 0000000..416a0a5
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88ds3103_priv.h
@@ -0,0 +1,413 @@
+/*
+ Montage Technology M88DS3103/3000 - DVBS/S2 Satellite demod driver
+
+ Copyright (C) 2011 Max nibble<nibble.max@xxxxxxxxx>
+ Copyright (C) 2010 Montage Technology<www.montage-tech.com>
+ Fix some bug and add M88DS3103 code, M88DS3000 code based on DS3000.c.
+
+ Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@xxxxxxxxx>
+
+ Copyright (C) 2009 TurboSight.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.
+*/
+
+#ifndef _M88DS3103_PRIV_H
+#define _M88DS3103_PRIV_H
+
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_INFO "m88ds3103: " args); \
+ } while (0)
+
+#define FW_DOWN_SIZE 32
+#define FW_DOWN_LOOP (8192/FW_DOWN_SIZE)
+#define DS3103_DEFAULT_FIRMWARE "dvb-fe-ds3103.fw"
+#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds300x.fw"
+#define MT_FE_MCLK_KHZ 96000 /* in kHz */
+#define MT_FE_CRYSTAL_KHZ 27000 /* in kHz */
+#define FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz 3000
+#define DS3000_ID 0x3000
+#define DS3103_ID 0x3103
+#define UNKNOW_ID 0x0000
+
+/* For M88DS3103 demod dvbs mode.*/
+static u8 ds3103_dvbs_init_tab[] = {
+ 0x23, 0x07,
+ 0x08, 0x03,
+ 0x0c, 0x02,
+ 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,
+ 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,
+};
+/* For M88DS3103 demod dvbs2 mode.*/
+static u8 ds3103_dvbs2_init_tab[] = {
+ 0x23, 0x07,
+ 0x08, 0x07,
+ 0x0c, 0x02,
+ 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,
+ 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,
+};
+
+/* For M88DS3000 demod dvbs mode.*/
+static u8 ds3000_dvbs_init_tab[] = {
+ 0x23, 0x05,
+ 0x08, 0x03,
+ 0x0c, 0x02,
+ 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, 0x40,
+ 0x4b, 0x04,
+ 0x4d, 0x91,
+ 0x5d, 0xc8,
+ 0x50, 0x77,
+ 0x51, 0x77,
+ 0x52, 0x36,
+ 0x53, 0x36,
+ 0x56, 0x01,
+ 0x63, 0x47,
+ 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, 0x00,
+ 0x77, 0xd1,
+ 0x78, 0x0c,
+ 0x79, 0x80,
+ 0x7f, 0x04,
+ 0x7c, 0x00,
+ 0x80, 0x86,
+ 0x81, 0xa6,
+ 0x85, 0x04,
+ 0xcd, 0xf4,
+ 0x90, 0x33,
+ 0xa0, 0x44,
+ 0xc0, 0x18,
+ 0xc3, 0x10,
+ 0xc4, 0x08,
+ 0xc5, 0x80,
+ 0xc6, 0x80,
+ 0xc7, 0x0a,
+ 0xc8, 0x1a,
+ 0xc9, 0x80,
+ 0xfe, 0xb6,
+ 0xe0, 0xf8,
+ 0xe6, 0x8b,
+ 0xd0, 0x40,
+ 0xf8, 0x20,
+ 0xfa, 0x0f,
+ 0xad, 0x20,
+ 0xae, 0x07,
+ 0xb8, 0x00,
+};
+
+/* For M88DS3000 demod dvbs2 mode.*/
+static u8 ds3000_dvbs2_init_tab[] = {
+ 0x23, 0x0f,
+ 0x08, 0x07,
+ 0x0c, 0x02,
+ 0x21, 0x54,
+ 0x25, 0x82,
+ 0x27, 0x31,
+ 0x30, 0x08,
+ 0x31, 0x32,
+ 0x32, 0x32,
+ 0x33, 0x35,
+ 0x35, 0xff,
+ 0x3a, 0x00,
+ 0x37, 0x10,
+ 0x38, 0x10,
+ 0x39, 0x02,
+ 0x42, 0x60,
+ 0x4a, 0x80,
+ 0x4b, 0x04,
+ 0x4d, 0x91,
+ 0x5d, 0x88,
+ 0x50, 0x36,
+ 0x51, 0x36,
+ 0x52, 0x36,
+ 0x53, 0x36,
+ 0x63, 0x60,
+ 0x64, 0x10,
+ 0x65, 0x10,
+ 0x68, 0x04,
+ 0x69, 0x29,
+ 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,
+ 0xa0, 0x44,
+ 0xc0, 0x08,
+ 0xc1, 0x10,
+ 0xc2, 0x08,
+ 0xc3, 0x10,
+ 0xc4, 0x08,
+ 0xc5, 0xf0,
+ 0xc6, 0xf0,
+ 0xc7, 0x0a,
+ 0xc8, 0x1a,
+ 0xc9, 0x80,
+ 0xca, 0x23,
+ 0xcb, 0x24,
+ 0xce, 0x74,
+ 0x56, 0x01,
+ 0x90, 0x03,
+ 0x76, 0x80,
+ 0x77, 0x42,
+ 0x78, 0x0a,
+ 0x79, 0x80,
+ 0xad, 0x40,
+ 0xae, 0x07,
+ 0x7f, 0xd4,
+ 0x7c, 0x00,
+ 0x80, 0xa8,
+ 0x81, 0xda,
+ 0x7c, 0x01,
+ 0x80, 0xda,
+ 0x81, 0xec,
+ 0x7c, 0x02,
+ 0x80, 0xca,
+ 0x81, 0xeb,
+ 0x7c, 0x03,
+ 0x80, 0xba,
+ 0x81, 0xdb,
+ 0x85, 0x08,
+ 0x86, 0x00,
+ 0x87, 0x02,
+ 0x89, 0x80,
+ 0x8b, 0x44,
+ 0x8c, 0xaa,
+ 0x8a, 0x10,
+ 0xba, 0x00,
+ 0xf5, 0x04,
+ 0xd2, 0x32,
+ 0xb8, 0x00,
+};
+
+struct m88ds3103_state {
+ struct i2c_adapter *i2c;
+ const struct m88ds3103_config *config;
+ struct dvb_frontend frontend;
+ u32 preBer;
+ u8 skip_fw_load;
+ u8 first_lock; /* The first time of signal lock */
+ u16 demod_id; /* demod chip type */
+ fe_delivery_system_t delivery_system;
+};
+
+#endif /* _M88DS3103_PRIV_H */
+
diff --git a/drivers/media/dvb/frontends/m88ts202x.c b/drivers/media/dvb/frontends/m88ts202x.c
new file mode 100644
index 0000000..6528dbc
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88ts202x.c
@@ -0,0 +1,590 @@
+/*
+ Montage Technology M88TS2022/2020 - DVBS/S2 Satellite tuner driver
+
+ Copyright (C) 2011 Max nibble<nibble.max@xxxxxxxxx>
+ Copyright (C) 2010 Montage Technology<www.montage-tech.com>
+ Fix some bug and add M88TS2022 code, M88TS2020 code based on DS3000.c.
+
+ Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@xxxxxxxxx>
+
+ Copyright (C) 2009 TurboSight.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 <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include "dvb_frontend.h"
+#include "m88ts202x.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates debugging (default:0)");
+
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_INFO "m88ts202x: " args); \
+ } while (0)
+
+#define MT_FE_CRYSTAL_KHZ 27000 /* in kHz */
+#define FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz 3000
+#define TS2020_ID 0x2020
+#define TS2022_ID 0x2022
+#define UNKNOW_ID 0x0000
+
+struct m88ts202x_state {
+ struct i2c_adapter *i2c;
+ const struct m88ts202x_config *config;
+ struct dvb_frontend *frontend;
+ struct m88ts202x_devctl *devctl;
+ u16 tuner_id; /* tuner chip type */
+};
+
+/*register operations.*/
+static int m88ts202x_writereg(struct m88ts202x_state *state, int reg, int data)
+{
+ struct dvb_frontend *fe = state->frontend;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr = 0x60,
+ .flags = 0, .buf = buf, .len = 2 };
+ int err;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ err = i2c_transfer(state->i2c, &msg, 1);
+
+ if (err != 1) {
+ printk("%s: writereg error(err == %i, reg == 0x%02x,"
+ " value == 0x%02x)\n", __func__, err, reg, data);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int m88ts202x_readreg(struct m88ts202x_state *state, u8 reg)
+{
+ int ret;
+ struct dvb_frontend *fe = state->frontend;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ { .addr = 0x60, .flags = 0,
+ .buf = b0, .len = 1 },
+ { .addr = 0x60, .flags = I2C_M_RD,
+ .buf = b1, .len = 1 }
+ };
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret);
+ return ret;
+ }
+
+ return b1[0];
+}
+
+static int m88ts202x_check_id(struct m88ts202x_state *state)
+{
+ int val_00;
+
+ /*check tuner id*/
+ val_00 = m88ts202x_readreg(state, 0x00);
+ printk(KERN_INFO "TS202x chip version[1]: %x found\n", val_00);
+ val_00 &= 0x03;
+ if (val_00 == 0) {
+ m88ts202x_writereg(state, 0x00, 0x01);
+ msleep(3);
+ }
+ m88ts202x_writereg(state, 0x00, 0x03);
+ msleep(5);
+ val_00 = m88ts202x_readreg(state, 0x00);
+ printk(KERN_INFO "TS202x chip version[2]: %x found\n", val_00);
+ val_00 &= 0xff;
+ if ((val_00 == 0x01) || (val_00 == 0x41) || (val_00 == 0x81))
+ state->tuner_id = TS2020_ID;
+ else if (((val_00 & 0xc0) == 0xc0) || (val_00 == 0x83))
+ state->tuner_id = TS2022_ID;
+ else
+ state->tuner_id = UNKNOW_ID;
+
+ return state->tuner_id;
+}
+
+/* wake up */
+static int m88ts202x_wakeup(struct dvb_frontend *fe)
+{
+ struct m88ts202x_state *state = fe->tuner_priv;
+ u8 val;
+
+ dprintk("%s()\n", __func__);
+
+ /*wake up tuner */
+ val = m88ts202x_readreg(state, 0x00) & 0xff;
+ if ((val & 0x01) == 0) {
+ m88ts202x_writereg(state, 0x00, 0x01);
+ msleep(50);
+ }
+ m88ts202x_writereg(state, 0x00, 0x03);
+ msleep(50);
+ return 0;
+}
+
+/*
+ * Initialise device
+ */
+static int m88ts202x_init(struct dvb_frontend *fe)
+{
+ struct m88ts202x_state *state = fe->tuner_priv;
+
+ dprintk("%s()\n", __func__);
+
+ if (state->tuner_id == TS2020_ID) {
+ /* TS2020 init */
+ m88ts202x_writereg(state, 0x42, 0x73);
+ msleep(2);
+ m88ts202x_writereg(state, 0x05, 0x01);
+ m88ts202x_writereg(state, 0x62, 0xb5);
+ m88ts202x_writereg(state, 0x07, 0x02);
+ m88ts202x_writereg(state, 0x08, 0x01);
+ } else if (state->tuner_id == TS2022_ID) {
+ /* TS2022 init */
+ m88ts202x_writereg(state, 0x62, 0x6c);
+ msleep(2);
+ m88ts202x_writereg(state, 0x42, 0x6c);
+ msleep(2);
+ m88ts202x_writereg(state, 0x7d, 0x9d);
+ m88ts202x_writereg(state, 0x7c, 0x9a);
+ m88ts202x_writereg(state, 0x7a, 0x76);
+
+ m88ts202x_writereg(state, 0x3b, 0x01);
+ m88ts202x_writereg(state, 0x63, 0x88);
+
+ m88ts202x_writereg(state, 0x61, 0x85);
+ m88ts202x_writereg(state, 0x22, 0x30);
+ m88ts202x_writereg(state, 0x30, 0x40);
+ m88ts202x_writereg(state, 0x20, 0x23);
+ m88ts202x_writereg(state, 0x24, 0x02);
+ m88ts202x_writereg(state, 0x12, 0xa0);
+ }
+ return 0;
+}
+
+static int m88ts202x_get_rfgain(struct dvb_frontend *fe, u16 *prfgain)
+{
+ struct m88ts202x_state *state = fe->tuner_priv;
+ u16 gain;
+ u8 gain1, gain2, gain3 = 0;
+
+ dprintk("%s()\n", __func__);
+
+ gain1 = m88ts202x_readreg(state, 0x3d) & 0x1f;
+ dprintk("%s: gain1 = 0x%02x\n", __func__, gain1);
+
+ if (gain1 > 15)
+ gain1 = 15;
+ gain2 = m88ts202x_readreg(state, 0x21) & 0x1f;
+ dprintk("%s: gain2 = 0x%02x\n", __func__, gain2);
+
+ if (state->tuner_id == TS2022_ID) {
+ gain3 = (m88ts202x_readreg(state, 0x66)>>3) & 0x07;
+ dprintk("%s: gain3 = 0x%02x\n", __func__, gain3);
+
+ if (gain2 > 16)
+ gain2 = 16;
+ if (gain2 < 2)
+ gain2 = 2;
+ if (gain3 > 6)
+ gain3 = 6;
+ } else {
+ if (gain2 > 13)
+ gain2 = 13;
+ gain3 = 0;
+ }
+
+ gain = gain1*23 + gain2*35 + gain3*29;
+ if (prfgain)
+ *prfgain = gain;
+
+ return 0;
+}
+
+
+static int m88ts202x_set_frequency(struct dvb_frontend *fe, s32 *pfreqOffset)
+{
+ struct m88ts202x_state *state = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf, div4, capCode, changePLL;
+ s32 offset_khz, lpf_offset_KHz;
+ u16 value, ndiv, lpf_coeff;
+ u32 f3db, gdiv28, realFreq;
+ u8 RFgain;
+
+ dprintk("%s() frequency=%d\n", __func__, c->frequency);
+
+ realFreq = c->frequency;
+ lpf_offset_KHz = 0;
+ if (c->symbol_rate < 5000000) {
+ lpf_offset_KHz = FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz;
+ realFreq += FREQ_OFFSET_AT_SMALL_SYM_RATE_KHz;
+ }
+
+ div4 = 0;
+ RFgain = 0;
+ if (state->tuner_id == TS2022_ID) {
+ m88ts202x_writereg(state, 0x10, 0x0a);
+ m88ts202x_writereg(state, 0x11, 0x40);
+ if (realFreq < 1103000) {
+ m88ts202x_writereg(state, 0x10, 0x1b);
+ div4 = 1;
+ ndiv = (realFreq * (6 + 8) * 4)
+ /MT_FE_CRYSTAL_KHZ;
+ } else {
+ ndiv = (realFreq * (6 + 8) * 2)
+ /MT_FE_CRYSTAL_KHZ;
+ }
+ ndiv = ndiv + ndiv%2;
+ if (ndiv < 4095)
+ ndiv = ndiv - 1024;
+ else if (ndiv < 6143)
+ ndiv = ndiv + 1024;
+ else
+ ndiv = ndiv + 3072;
+
+ m88ts202x_writereg(state, 0x01,
+ (ndiv & 0x3f00) >> 8);
+
+ } else {
+ m88ts202x_writereg(state, 0x10, 0x00);
+ if (realFreq < 1146000) {
+ m88ts202x_writereg(state, 0x10, 0x11);
+ div4 = 1;
+ ndiv = (realFreq * (6 + 8) * 4) / MT_FE_CRYSTAL_KHZ;
+ } else {
+ m88ts202x_writereg(state, 0x10, 0x01);
+ ndiv = (realFreq * (6 + 8) * 2) / MT_FE_CRYSTAL_KHZ;
+ }
+ ndiv = ndiv + ndiv%2;
+ ndiv = ndiv - 1024;
+ m88ts202x_writereg(state, 0x01, (ndiv>>8)&0x0f);
+ }
+ /* set pll */
+ m88ts202x_writereg(state, 0x02, ndiv & 0x00ff);
+ m88ts202x_writereg(state, 0x03, 0x06);
+ m88ts202x_writereg(state, 0x51, 0x0f);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x10);
+ m88ts202x_writereg(state, 0x50, 0x00);
+
+ if (state->tuner_id == TS2022_ID) {
+ if ((realFreq >= 1650000) && (realFreq <= 1850000)) {
+ msleep(5);
+ value = m88ts202x_readreg(state, 0x14);
+ value &= 0x7f;
+ if (value < 64) {
+ m88ts202x_writereg(state, 0x10, 0x82);
+ m88ts202x_writereg(state, 0x11, 0x6f);
+
+ m88ts202x_writereg(state, 0x51, 0x0f);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x10);
+ m88ts202x_writereg(state, 0x50, 0x00);
+ }
+ }
+ msleep(5);
+ value = m88ts202x_readreg(state, 0x14);
+ value &= 0x1f;
+
+ if (value > 19) {
+ value = m88ts202x_readreg(state, 0x10);
+ value &= 0x1d;
+ m88ts202x_writereg(state, 0x10, value);
+ }
+ } else {
+ msleep(5);
+ value = m88ts202x_readreg(state, 0x66);
+ changePLL = (((value & 0x80) >> 7) != div4);
+
+ if (changePLL) {
+ m88ts202x_writereg(state, 0x10, 0x11);
+ div4 = 1;
+ ndiv = (realFreq * (6 + 8) * 4)/MT_FE_CRYSTAL_KHZ;
+ ndiv = ndiv + ndiv%2;
+ ndiv = ndiv - 1024;
+
+ m88ts202x_writereg(state, 0x01, (ndiv>>8) & 0x0f);
+ m88ts202x_writereg(state, 0x02, ndiv & 0xff);
+ m88ts202x_writereg(state, 0x51, 0x0f);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x10);
+ m88ts202x_writereg(state, 0x50, 0x00);
+ }
+ }
+ /*set the RF gain*/
+ if (state->tuner_id == TS2020_ID)
+ m88ts202x_writereg(state, 0x60, 0x79);
+
+ m88ts202x_writereg(state, 0x51, 0x17);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x08);
+ m88ts202x_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ if (state->tuner_id == TS2020_ID) {
+ RFgain = m88ts202x_readreg(state, 0x3d);
+ RFgain &= 0x0f;
+ if (RFgain < 15) {
+ if (RFgain < 4)
+ RFgain = 0;
+ else
+ RFgain = RFgain - 3;
+ value = ((RFgain << 3) | 0x01) & 0x79;
+ m88ts202x_writereg(state, 0x60, value);
+ m88ts202x_writereg(state, 0x51, 0x17);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x08);
+ m88ts202x_writereg(state, 0x50, 0x00);
+ }
+ }
+ /* set the LPF */
+ if (state->tuner_id == TS2022_ID) {
+ m88ts202x_writereg(state, 0x25, 0x00);
+ m88ts202x_writereg(state, 0x27, 0x70);
+ m88ts202x_writereg(state, 0x41, 0x09);
+ m88ts202x_writereg(state, 0x08, 0x0b);
+ }
+
+ f3db = ((c->symbol_rate / 1000) * 135) / 200 + 2000;
+ f3db += lpf_offset_KHz;
+ if (f3db < 7000)
+ f3db = 7000;
+ if (f3db > 40000)
+ f3db = 40000;
+
+ gdiv28 = (MT_FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000;
+ m88ts202x_writereg(state, 0x04, gdiv28 & 0xff);
+ m88ts202x_writereg(state, 0x51, 0x1b);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x04);
+ m88ts202x_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ value = m88ts202x_readreg(state, 0x26);
+ capCode = value & 0x3f;
+ if (state->tuner_id == TS2022_ID) {
+ m88ts202x_writereg(state, 0x41, 0x0d);
+
+ m88ts202x_writereg(state, 0x51, 0x1b);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x04);
+ m88ts202x_writereg(state, 0x50, 0x00);
+
+ msleep(2);
+
+ value = m88ts202x_readreg(state, 0x26);
+ value &= 0x3f;
+ value = (capCode + value) / 2;
+ } else
+ value = capCode;
+
+ gdiv28 = gdiv28 * 207 / (value * 2 + 151);
+ mlpf_max = gdiv28 * 135 / 100;
+ mlpf_min = gdiv28 * 78 / 100;
+ if (mlpf_max > 63)
+ mlpf_max = 63;
+
+ if (state->tuner_id == TS2022_ID)
+ lpf_coeff = 3200;
+ else
+ lpf_coeff = 2766;
+
+ nlpf = (f3db * gdiv28 * 2 / lpf_coeff
+ / (MT_FE_CRYSTAL_KHZ / 1000) + 1) / 2 ;
+ if (nlpf > 23)
+ nlpf = 23;
+ if (nlpf < 1)
+ nlpf = 1;
+
+ lpf_mxdiv = (nlpf * (MT_FE_CRYSTAL_KHZ / 1000)
+ * lpf_coeff * 2 / f3db + 1) / 2;
+
+ if (lpf_mxdiv < mlpf_min) {
+ nlpf++;
+ lpf_mxdiv = (nlpf * (MT_FE_CRYSTAL_KHZ / 1000)
+ * lpf_coeff * 2 / f3db + 1) / 2;
+ }
+
+ if (lpf_mxdiv > mlpf_max)
+ lpf_mxdiv = mlpf_max;
+
+ m88ts202x_writereg(state, 0x04, lpf_mxdiv);
+ m88ts202x_writereg(state, 0x06, nlpf);
+ m88ts202x_writereg(state, 0x51, 0x1b);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x04);
+ m88ts202x_writereg(state, 0x50, 0x00);
+ msleep(5);
+
+ if (state->tuner_id == TS2022_ID) {
+ msleep(2);
+ value = m88ts202x_readreg(state, 0x26);
+ capCode = value & 0x3f;
+
+ m88ts202x_writereg(state, 0x41, 0x09);
+
+ m88ts202x_writereg(state, 0x51, 0x1b);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x04);
+ m88ts202x_writereg(state, 0x50, 0x00);
+
+ msleep(2);
+ value = m88ts202x_readreg(state, 0x26);
+ value &= 0x3f;
+ value = (capCode + value) / 2;
+
+ value = value | 0x80;
+ m88ts202x_writereg(state, 0x25, value);
+ m88ts202x_writereg(state, 0x27, 0x30);
+
+ m88ts202x_writereg(state, 0x08, 0x09);
+ }
+
+ /* Set the BB gain */
+ m88ts202x_writereg(state, 0x51, 0x1e);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x01);
+ m88ts202x_writereg(state, 0x50, 0x00);
+ if (state->tuner_id == TS2020_ID) {
+ if (RFgain == 15) {
+ msleep(40);
+ value = m88ts202x_readreg(state, 0x21);
+ value &= 0x0f;
+ if (value < 3) {
+ m88ts202x_writereg(state, 0x60, 0x61);
+ m88ts202x_writereg(state, 0x51, 0x17);
+ m88ts202x_writereg(state, 0x51, 0x1f);
+ m88ts202x_writereg(state, 0x50, 0x08);
+ m88ts202x_writereg(state, 0x50, 0x00);
+ }
+ }
+ }
+ msleep(60);
+
+ offset_khz = (ndiv - ndiv % 2 + 1024) * MT_FE_CRYSTAL_KHZ
+ / (6 + 8) / (div4 + 1) / 2 - realFreq;
+
+ *pfreqOffset = offset_khz + lpf_offset_KHz;
+
+ return 0;
+}
+
+/* Put device to sleep */
+static int m88ts202x_sleep(struct dvb_frontend *fe)
+{
+ struct m88ts202x_state *state = fe->tuner_priv;
+
+ dprintk("%s()\n", __func__);
+
+ m88ts202x_writereg(state, 0x00, 0x00);
+
+ return 0;
+}
+
+static int m88ts202x_release(struct dvb_frontend *fe)
+{
+ struct m88ts202x_state *priv = fe->tuner_priv;
+ dprintk("%s()\n", __func__);
+
+ kfree(priv);
+ fe->tuner_priv = NULL;
+
+ return 0;
+}
+static struct m88ts202x_devctl m88ts202x_ctl = {
+ .tuner_init = m88ts202x_init,
+ .tuner_sleep = m88ts202x_sleep,
+ .tuner_wakeup = m88ts202x_wakeup,
+ .tuner_set_frequency = m88ts202x_set_frequency,
+ .tuner_get_rfgain = m88ts202x_get_rfgain,
+};
+
+static const struct dvb_tuner_ops m88ts202x_tuner_ops = {
+ .info = {
+ .name = "Montage M88TS202x",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_step = 50000,
+ },
+
+ .release = m88ts202x_release,
+};
+
+struct m88ts202x_devctl *m88ts202x_attach(struct dvb_frontend *fe,
+ const struct m88ts202x_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct m88ts202x_state *state = NULL;
+
+ dprintk("%s\n", __func__);
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct m88ts202x_state), GFP_KERNEL);
+ if (state == NULL) {
+ printk(KERN_ERR "Unable to kmalloc\n");
+ goto error2;
+ }
+
+ state->config = config;
+ state->i2c = i2c;
+ state->frontend = fe;
+ state->devctl = &m88ts202x_ctl;
+
+ /* check chip id */
+ if (m88ts202x_check_id(state) == UNKNOW_ID) {
+ printk(KERN_ERR "Unable to find Montage tuner\n");
+ goto error3;
+ }
+
+ memcpy(&fe->ops.tuner_ops, &m88ts202x_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = state;
+
+ m88ts202x_init(fe);
+
+ return state->devctl;
+
+error3:
+ kfree(state);
+error2:
+ return NULL;
+}
+EXPORT_SYMBOL(m88ts202x_attach);
+
+MODULE_DESCRIPTION("DVB tuner module for Montage TS2022");
+MODULE_AUTHOR("Max nibble");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/m88ts202x.h b/drivers/media/dvb/frontends/m88ts202x.h
new file mode 100644
index 0000000..f001a31
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88ts202x.h
@@ -0,0 +1,63 @@
+/*
+ Montage Technology M88TS2022/2020 - DVBS/S2 Satellite tuner driver
+
+ Copyright (C) 2011 Max nibble<nibble.max@xxxxxxxxx>
+ Copyright (C) 2010 Montage Technology<www.montage-tech.com>
+ Fix some bug and add M88TS2022 code, M88TS2020 code based on DS3000.c.
+
+ Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@xxxxxxxxx>
+
+ Copyright (C) 2009 TurboSight.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.
+*/
+
+#ifndef _M88TS202X_H
+#define _M88TS202X_H
+
+#include <linux/dvb/frontend.h>
+
+struct m88ts202x_config {
+ u8 bypasson; /* 1- Enable bypass loop out */
+ u8 clkout; /* 1- Enable clk output */
+ u8 clkdiv; /* 0- Output 27MHz, 1-Output 13.5MHz */
+};
+
+struct m88ts202x_devctl {
+
+ int (*tuner_init) (struct dvb_frontend *fe);
+ int (*tuner_sleep) (struct dvb_frontend *fe);
+ int (*tuner_wakeup) (struct dvb_frontend *fe);
+ int (*tuner_set_frequency) (struct dvb_frontend *fe, s32 *pfreqOffset);
+ int (*tuner_get_rfgain) (struct dvb_frontend *fe, u16 *prfgain);
+};
+
+#if defined(CONFIG_DVB_M88TS202X) || \
+ (defined(CONFIG_DVB_M88TS202X_MODULE) && defined(MODULE))
+extern struct m88ts202x_devctl *m88ts202x_attach(
+ struct dvb_frontend *fe,
+ const struct m88ts202x_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct m88ts202x_devctl *m88ts202x_attach(
+ struct dvb_frontend *fe,
+ const struct m88ts202x_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_M88TS202X */
+#endif /* _M88TS202X_H */
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
[Linux Input]
[Video for Linux]
[Mplayer Users]
[Linux USB Devel]
[Linux Audio Users]
[Photos]
[Yosemite Photos]
[Linux Kernel]
[Linux SCSI]
[XFree86]
[Devices]
[Yosemite Backpacking]