mirror of
https://github.com/LibreELEC/LibreELEC.tv
synced 2025-09-24 19:46:01 +07:00
3485 lines
101 KiB
Diff
3485 lines
101 KiB
Diff
From d89d7ff6fd6c064cfae0100b0a06df1340c87a55 Mon Sep 17 00:00:00 2001
|
|
From: Stefan Saraev <stefan@saraev.ca>
|
|
Date: Tue, 5 Aug 2014 13:56:41 +0300
|
|
Subject: [PATCH] dvbsky s950v3 s952v3
|
|
|
|
---
|
|
drivers/media/dvb-frontends/Kconfig | 7 +
|
|
drivers/media/dvb-frontends/Makefile | 1 +
|
|
drivers/media/dvb-frontends/dvbsky_m88rs6000.c | 1662 ++++++++++++++++++++
|
|
drivers/media/dvb-frontends/dvbsky_m88rs6000.h | 39 +
|
|
.../media/dvb-frontends/dvbsky_m88rs6000_priv.h | 210 +++
|
|
drivers/media/pci/Kconfig | 1 +
|
|
drivers/media/pci/Makefile | 1 +
|
|
drivers/media/pci/smipcie/Kconfig | 12 +
|
|
drivers/media/pci/smipcie/Makefile | 3 +
|
|
drivers/media/pci/smipcie/smipcie.c | 1131 +++++++++++++
|
|
drivers/media/pci/smipcie/smipcie.h | 304 ++++
|
|
11 files changed, 3371 insertions(+), 0 deletions(-)
|
|
create mode 100644 drivers/media/dvb-frontends/dvbsky_m88rs6000.c
|
|
create mode 100644 drivers/media/dvb-frontends/dvbsky_m88rs6000.h
|
|
create mode 100644 drivers/media/dvb-frontends/dvbsky_m88rs6000_priv.h
|
|
create mode 100644 drivers/media/pci/smipcie/Kconfig
|
|
create mode 100644 drivers/media/pci/smipcie/Makefile
|
|
create mode 100644 drivers/media/pci/smipcie/smipcie.c
|
|
create mode 100644 drivers/media/pci/smipcie/smipcie.h
|
|
|
|
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
|
|
index 694b35a..7288ce9 100644
|
|
--- a/drivers/media/dvb-frontends/Kconfig
|
|
+++ b/drivers/media/dvb-frontends/Kconfig
|
|
@@ -262,6 +262,13 @@ config DVB_DVBSKY_M88DC2800
|
|
help
|
|
A DVB-C tuner module. Say Y when you want to support this frontend.
|
|
|
|
+config DVB_DVBSKY_M88RS6000
|
|
+ tristate "Montage M88RS6000 (DVBSky)"
|
|
+ depends on DVB_CORE && I2C
|
|
+ default m if !MEDIA_SUBDRV_AUTOSELECT
|
|
+ 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 4c7427d..646d24a 100644
|
|
--- a/drivers/media/dvb-frontends/Makefile
|
|
+++ b/drivers/media/dvb-frontends/Makefile
|
|
@@ -115,3 +115,4 @@ obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
|
|
obj-$(CONFIG_DVB_AF9033) += af9033.o
|
|
obj-$(CONFIG_DVB_DVBSKY_M88DS3103) += dvbsky_m88ds3103.o
|
|
obj-$(CONFIG_DVB_DVBSKY_M88DC2800) += dvbsky_m88dc2800.o
|
|
+obj-$(CONFIG_DVB_DVBSKY_M88RS6000) += dvbsky_m88rs6000.o
|
|
diff --git a/drivers/media/dvb-frontends/dvbsky_m88rs6000.c b/drivers/media/dvb-frontends/dvbsky_m88rs6000.c
|
|
new file mode 100644
|
|
index 0000000..b999cac
|
|
--- /dev/null
|
|
+++ b/drivers/media/dvb-frontends/dvbsky_m88rs6000.c
|
|
@@ -0,0 +1,1662 @@
|
|
+/*
|
|
+ Montage Technology M88RS6000
|
|
+ - DVBS/S2 Satellite demod/tuner driver
|
|
+ Copyright (C) 2014 Max Nibble <nibble.max@gmail.com>
|
|
+
|
|
+ */
|
|
+
|
|
+#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 "dvbsky_m88rs6000.h"
|
|
+#include "dvbsky_m88rs6000_priv.h"
|
|
+
|
|
+static int debug;
|
|
+module_param(debug, int, 0644);
|
|
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
|
|
+
|
|
+#define dprintk(args...) \
|
|
+ do { \
|
|
+ if (debug) \
|
|
+ printk(KERN_INFO "m88rs6000: " args); \
|
|
+ } while (0)
|
|
+
|
|
+/*demod register operations.*/
|
|
+static int dvbsky_m88rs6000_writereg(struct dvbsky_m88rs6000_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 ret;
|
|
+
|
|
+ if (debug > 1)
|
|
+ printk("m88rs6000: %s: write reg 0x%02x, value 0x%02x\n",
|
|
+ __func__, reg, data);
|
|
+
|
|
+ ret = i2c_transfer(state->i2c, &msg, 1);
|
|
+ if (ret != 1) {
|
|
+ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x,"
|
|
+ " value == 0x%02x)\n", __func__, ret, reg, data);
|
|
+ return -EREMOTEIO;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_readreg(struct dvbsky_m88rs6000_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 "m88rs6000: read reg 0x%02x, value 0x%02x\n",
|
|
+ reg, b1[0]);
|
|
+
|
|
+ return b1[0];
|
|
+}
|
|
+
|
|
+/*tuner register operations.*/
|
|
+static int dvbsky_m88rs6000_tuner_writereg(struct dvbsky_m88rs6000_state *state, int reg, int data)
|
|
+{
|
|
+ u8 buf[] = { reg, data };
|
|
+ struct i2c_msg msg = { .addr = state->tuner_addr,
|
|
+ .flags = 0, .buf = buf, .len = 2 };
|
|
+ int ret;
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0x03, 0x11);
|
|
+ ret = i2c_transfer(state->i2c, &msg, 1);
|
|
+
|
|
+ if (ret != 1) {
|
|
+ printk("%s: writereg error(err == %i, reg == 0x%02x,"
|
|
+ " value == 0x%02x)\n", __func__, ret, reg, data);
|
|
+ return -EREMOTEIO;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_tuner_readreg(struct dvbsky_m88rs6000_state *state, u8 reg)
|
|
+{
|
|
+ int ret;
|
|
+ u8 b0[] = { reg };
|
|
+ u8 b1[] = { 0 };
|
|
+ struct i2c_msg msg[] = {
|
|
+ { .addr = state->tuner_addr, .flags = 0,
|
|
+ .buf = b0, .len = 1 },
|
|
+ { .addr = state->tuner_addr, .flags = I2C_M_RD,
|
|
+ .buf = b1, .len = 1 }
|
|
+ };
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0x03, (0x11 + state->config->tuner_readstops));
|
|
+ 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];
|
|
+}
|
|
+
|
|
+/* Bulk demod I2C write, for firmware download. */
|
|
+static int dvbsky_m88rs6000_writeregN(struct dvbsky_m88rs6000_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("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 "m88rs6000: %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 dvbsky_m88rs6000_load_firmware(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_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 */
|
|
+ printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__,
|
|
+ RS6000_DEFAULT_FIRMWARE);
|
|
+ ret = request_firmware(&fw, RS6000_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. */
|
|
+ dvbsky_m88rs6000_writereg(state, 0xb2, 0x01);
|
|
+ /* split firmware to download.*/
|
|
+ for(i = 0; i < FW_DOWN_LOOP; i++){
|
|
+ ret = dvbsky_m88rs6000_writeregN(state, 0xb0, &(fw->data[FW_DOWN_SIZE*i]), FW_DOWN_SIZE);
|
|
+ if(ret != 1) break;
|
|
+ }
|
|
+ /* start internal mcu. */
|
|
+ if(ret == 1)
|
|
+ dvbsky_m88rs6000_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 dvbsky_m88rs6000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+ u8 data;
|
|
+
|
|
+ dprintk("%s(%d)\n", __func__, voltage);
|
|
+
|
|
+ dprintk("m88rs6000:pin_ctrl = (%02x)\n", state->config->pin_ctrl);
|
|
+
|
|
+ if(state->config->set_voltage)
|
|
+ state->config->set_voltage(fe, voltage);
|
|
+
|
|
+ data = dvbsky_m88rs6000_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;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa2, data);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_read_status(struct dvb_frontend *fe, fe_status_t* status)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+ int lock = 0;
|
|
+
|
|
+ *status = 0;
|
|
+
|
|
+ switch (state->delivery_system){
|
|
+ case SYS_DVBS:
|
|
+ lock = dvbsky_m88rs6000_readreg(state, 0xd1);
|
|
+ dprintk("%s: SYS_DVBS status=%x.\n", __func__, lock);
|
|
+
|
|
+ if ((lock & 0x07) == 0x07){
|
|
+ /*if((dvbsky_m88rs6000_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 = dvbsky_m88rs6000_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 dvbsky_m88rs6000_read_ber(struct dvb_frontend *fe, u32* ber)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_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:
|
|
+ dvbsky_m88rs6000_writereg(state, 0xf9, 0x04);
|
|
+ tmp3 = dvbsky_m88rs6000_readreg(state, 0xf8);
|
|
+ if ((tmp3&0x10) == 0){
|
|
+ tmp1 = dvbsky_m88rs6000_readreg(state, 0xf7);
|
|
+ tmp2 = dvbsky_m88rs6000_readreg(state, 0xf6);
|
|
+ tmp3 |= 0x10;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xf8, tmp3);
|
|
+ state->preBer = (tmp1<<8) | tmp2;
|
|
+ }
|
|
+ break;
|
|
+ case SYS_DVBS2:
|
|
+ tmp1 = dvbsky_m88rs6000_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 = dvbsky_m88rs6000_readreg(state, 0xd7) & 0xff;
|
|
+ tmp2 = dvbsky_m88rs6000_readreg(state, 0xd6) & 0xff;
|
|
+ tmp3 = dvbsky_m88rs6000_readreg(state, 0xd5) & 0xff;
|
|
+ ldpc_frame_cnt = (tmp1 << 16) | (tmp2 << 8) | tmp3;
|
|
+
|
|
+ tmp1 = dvbsky_m88rs6000_readreg(state, 0xf8) & 0xff;
|
|
+ tmp2 = dvbsky_m88rs6000_readreg(state, 0xf7) & 0xff;
|
|
+ pre_err_packags = tmp1<<8 | tmp2;
|
|
+
|
|
+ if (ldpc_frame_cnt > 1000){
|
|
+ dvbsky_m88rs6000_writereg(state, 0xd1, 0x01);
|
|
+ dvbsky_m88rs6000_writereg(state, 0xf9, 0x01);
|
|
+ dvbsky_m88rs6000_writereg(state, 0xf9, 0x00);
|
|
+ dvbsky_m88rs6000_writereg(state, 0xd1, 0x00);
|
|
+ state->preBer = pre_err_packags;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ *ber = state->preBer;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_read_signal_strength(struct dvb_frontend *fe,
|
|
+ u16 *signal_strength)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+
|
|
+ int val;
|
|
+
|
|
+ //u32 RF_GS = 290, IF_GS = 290, BB_GS = 290;
|
|
+ u32 PGA2_cri_GS = 46, PGA2_crf_GS = 290, TIA_GS = 290;
|
|
+ u32 RF_GC = 1200, IF_GC = 1100, BB_GC = 300, PGA2_GC = 300, TIA_GC = 300;
|
|
+ u32 PGA2_cri = 0, PGA2_crf = 0;
|
|
+ u32 RFG = 0, IFG = 0, BBG = 0, PGA2G = 0, TIAG = 0;
|
|
+
|
|
+ u32 i = 0;
|
|
+
|
|
+ u32 RFGS[13] = {0, 245, 266, 268, 270, 285, 298, 295, 283, 285, 285, 300, 300};
|
|
+ u32 IFGS[12] = {0, 300, 230, 270, 270, 285, 295, 285, 290, 295, 295, 310};
|
|
+ u32 BBGS[14] = {0, 286, 275, 290, 294, 300, 290, 290, 285, 283, 260, 295, 290, 260};
|
|
+
|
|
+ dprintk("%s()\n", __func__);
|
|
+
|
|
+ val = dvbsky_m88rs6000_tuner_readreg(state, 0x5A);
|
|
+ RF_GC = val & 0x0f;
|
|
+
|
|
+ val = dvbsky_m88rs6000_tuner_readreg(state, 0x5F);
|
|
+ IF_GC = val & 0x0f;
|
|
+
|
|
+ val = dvbsky_m88rs6000_tuner_readreg(state, 0x3F);
|
|
+ TIA_GC = (val >> 4) & 0x07;
|
|
+
|
|
+ val = dvbsky_m88rs6000_tuner_readreg(state, 0x77);
|
|
+ BB_GC = (val >> 4) & 0x0f;
|
|
+
|
|
+ val = dvbsky_m88rs6000_tuner_readreg(state, 0x76);
|
|
+ PGA2_GC = val & 0x3f;
|
|
+ PGA2_cri = PGA2_GC >> 2;
|
|
+ PGA2_crf = PGA2_GC & 0x03;
|
|
+
|
|
+ for(i = 0; i <= RF_GC; i++) {
|
|
+ RFG += RFGS[i];
|
|
+ }
|
|
+
|
|
+ if(RF_GC == 0) RFG += 400;
|
|
+ if(RF_GC == 1) RFG += 300;
|
|
+ if(RF_GC == 2) RFG += 200;
|
|
+ if(RF_GC == 3) RFG += 100;
|
|
+
|
|
+ for(i = 0; i <= IF_GC; i++) {
|
|
+ IFG += IFGS[i];
|
|
+ }
|
|
+
|
|
+ TIAG = TIA_GC * TIA_GS;
|
|
+
|
|
+ for(i = 0; i <= BB_GC; i++) {
|
|
+ BBG += BBGS[i];
|
|
+ }
|
|
+
|
|
+ PGA2G = PGA2_cri * PGA2_cri_GS + PGA2_crf * PGA2_crf_GS;
|
|
+
|
|
+ *signal_strength = RFG + IFG - TIAG + BBG + PGA2G;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static int dvbsky_m88rs6000_read_snr(struct dvb_frontend *fe, u16 *p_snr)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_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 = dvbsky_m88rs6000_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 = dvbsky_m88rs6000_readreg(state, 0x8c) & 0xff;
|
|
+ npow2 = dvbsky_m88rs6000_readreg(state, 0x8d) & 0xff;
|
|
+ npow += (((npow1 & 0x3f) + (u16)(npow2 << 6)) >> 2);
|
|
+
|
|
+ spow1 = dvbsky_m88rs6000_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 dvbsky_m88rs6000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+ u8 tmp1, tmp2, tmp3, data;
|
|
+
|
|
+ dprintk("%s()\n", __func__);
|
|
+
|
|
+ switch (state->delivery_system) {
|
|
+ case SYS_DVBS:
|
|
+ data = dvbsky_m88rs6000_readreg(state, 0xf8);
|
|
+ data |= 0x40;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xf8, data);
|
|
+ tmp1 = dvbsky_m88rs6000_readreg(state, 0xf5);
|
|
+ tmp2 = dvbsky_m88rs6000_readreg(state, 0xf4);
|
|
+ *ucblocks = (tmp1 <<8) | tmp2;
|
|
+ data &= ~0x20;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xf8, data);
|
|
+ data |= 0x20;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xf8, data);
|
|
+ data &= ~0x40;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xf8, data);
|
|
+ break;
|
|
+ case SYS_DVBS2:
|
|
+ tmp1 = dvbsky_m88rs6000_readreg(state, 0xda);
|
|
+ tmp2 = dvbsky_m88rs6000_readreg(state, 0xd9);
|
|
+ tmp3 = dvbsky_m88rs6000_readreg(state, 0xd8);
|
|
+ *ucblocks = (tmp1 <<16)|(tmp2 <<8)|tmp3;
|
|
+ data = dvbsky_m88rs6000_readreg(state, 0xd1);
|
|
+ data |= 0x01;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xd1, data);
|
|
+ data &= ~0x01;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xd1, data);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_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 = dvbsky_m88rs6000_readreg(state, 0xa1);
|
|
+ data_a2 = dvbsky_m88rs6000_readreg(state, 0xa2);
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa2, data_a2);
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa1, data_a1);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_send_diseqc_msg(struct dvb_frontend *fe,
|
|
+ struct dvb_diseqc_master_cmd *d)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+ int i, ret = 0;
|
|
+ u8 tmp, time_out;
|
|
+
|
|
+ /* Dump DiSEqC message */
|
|
+ if (debug) {
|
|
+ printk(KERN_INFO "m88rs6000: %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 = dvbsky_m88rs6000_readreg(state, 0xa2);
|
|
+ tmp &= ~0xc0;
|
|
+ tmp &= ~0x20;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa2, tmp);
|
|
+
|
|
+ for (i = 0; i < d->msg_len; i ++)
|
|
+ dvbsky_m88rs6000_writereg(state, (0xa3+i), d->msg[i]);
|
|
+
|
|
+ tmp = dvbsky_m88rs6000_readreg(state, 0xa1);
|
|
+ tmp &= ~0x38;
|
|
+ tmp &= ~0x40;
|
|
+ tmp |= ((d->msg_len-1) << 3) | 0x07;
|
|
+ tmp &= ~0x80;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa1, tmp);
|
|
+ /* 1.5 * 9 * 8 = 108ms */
|
|
+ time_out = 150;
|
|
+ while (time_out > 0){
|
|
+ msleep(10);
|
|
+ time_out -= 10;
|
|
+ tmp = dvbsky_m88rs6000_readreg(state, 0xa1);
|
|
+ if ((tmp & 0x40) == 0)
|
|
+ break;
|
|
+ }
|
|
+ if (time_out == 0){
|
|
+ tmp = dvbsky_m88rs6000_readreg(state, 0xa1);
|
|
+ tmp &= ~0x80;
|
|
+ tmp |= 0x40;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa1, tmp);
|
|
+ ret = 1;
|
|
+ }
|
|
+ tmp = dvbsky_m88rs6000_readreg(state, 0xa2);
|
|
+ tmp &= ~0xc0;
|
|
+ tmp |= 0x80;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa2, tmp);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static int dvbsky_m88rs6000_diseqc_send_burst(struct dvb_frontend *fe,
|
|
+ fe_sec_mini_cmd_t burst)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+ u8 val, time_out;
|
|
+
|
|
+ dprintk("%s()\n", __func__);
|
|
+
|
|
+ val = dvbsky_m88rs6000_readreg(state, 0xa2);
|
|
+ val &= ~0xc0;
|
|
+ val &= 0xdf; /* Normal mode */
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa2, val);
|
|
+ /* DiSEqC burst */
|
|
+ if (burst == SEC_MINI_B)
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa1, 0x01);
|
|
+ else
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa1, 0x02);
|
|
+
|
|
+ msleep(13);
|
|
+
|
|
+ time_out = 5;
|
|
+ do{
|
|
+ val = dvbsky_m88rs6000_readreg(state, 0xa1);
|
|
+ if ((val & 0x40) == 0)
|
|
+ break;
|
|
+ msleep(1);
|
|
+ time_out --;
|
|
+ } while (time_out > 0);
|
|
+
|
|
+ val = dvbsky_m88rs6000_readreg(state, 0xa2);
|
|
+ val &= ~0xc0;
|
|
+ val |= 0x80;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xa2, val);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void dvbsky_m88rs6000_release(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+
|
|
+ dprintk("%s\n", __func__);
|
|
+ kfree(state);
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_check_id(struct dvbsky_m88rs6000_state *state)
|
|
+{
|
|
+ int val_00, val_01, val_02;
|
|
+
|
|
+ /*check demod id*/
|
|
+ val_00 = dvbsky_m88rs6000_readreg(state, 0x00);
|
|
+ val_01 = dvbsky_m88rs6000_readreg(state, 0x01);
|
|
+ val_02 = dvbsky_m88rs6000_readreg(state, 0x02);
|
|
+ printk(KERN_INFO "RS6000 chip, demod id=%x, version=%x.\n", val_00, (val_02 << 8 | val_01));
|
|
+
|
|
+ val_01 = dvbsky_m88rs6000_tuner_readreg(state, 0x01);
|
|
+ printk(KERN_INFO "RS6000 chip, tuner id=%x.\n", val_01);
|
|
+
|
|
+ state->demod_id = 0;
|
|
+ if(val_00 == 0xE8) {
|
|
+ state->demod_id = RS6000_ID;
|
|
+ }
|
|
+
|
|
+ return state->demod_id;
|
|
+}
|
|
+
|
|
+static struct dvb_frontend_ops dvbsky_m88rs6000_ops;
|
|
+static int dvbsky_m88rs6000_initilaze(struct dvb_frontend *fe);
|
|
+
|
|
+struct dvb_frontend *dvbsky_m88rs6000_attach(const struct dvbsky_m88rs6000_config *config,
|
|
+ struct i2c_adapter *i2c)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = NULL;
|
|
+
|
|
+ dprintk("%s\n", __func__);
|
|
+
|
|
+ /* allocate memory for the internal state */
|
|
+ state = kzalloc(sizeof(struct dvbsky_m88rs6000_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.*/
|
|
+ state->iMclkKHz = 96000;
|
|
+
|
|
+ memcpy(&state->frontend.ops, &dvbsky_m88rs6000_ops,
|
|
+ sizeof(struct dvb_frontend_ops));
|
|
+ state->frontend.demodulator_priv = state;
|
|
+
|
|
+ /* check demod id */
|
|
+ if(dvbsky_m88rs6000_initilaze(&state->frontend)){
|
|
+ printk(KERN_ERR "Unable to find Montage RS6000.\n");
|
|
+ goto error3;
|
|
+ }
|
|
+
|
|
+ return &state->frontend;
|
|
+
|
|
+error3:
|
|
+ kfree(state);
|
|
+error2:
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(dvbsky_m88rs6000_attach);
|
|
+
|
|
+static int dvbsky_m88rs6000_tuner_set_pll_freq(struct dvbsky_m88rs6000_state *state, u32 tuner_freq_MHz)
|
|
+{
|
|
+ u32 fcry_KHz, ulNDiv1, ulNDiv2, ulNDiv;
|
|
+ u8 refDiv, ucLoDiv1, ucLomod1, ucLoDiv2, ucLomod2, ucLoDiv, ucLomod;
|
|
+ u8 reg27, reg29, reg2d, reg2e, reg36, reg42, reg42buf, reg83;
|
|
+
|
|
+ fcry_KHz = MT_FE_CRYSTAL_KHZ;
|
|
+ refDiv = 27;
|
|
+ reg36 = refDiv - 8;
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x36, reg36);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x31, 0x00);
|
|
+
|
|
+ if(reg36 == 19) {
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x2c, 0x02);
|
|
+ } else {
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x2c, 0x00);
|
|
+ }
|
|
+
|
|
+ if(tuner_freq_MHz >= 1550) {
|
|
+ ucLoDiv1 = 2;
|
|
+ ucLomod1 = 0;
|
|
+ ucLoDiv2 = 2;
|
|
+ ucLomod2 = 0;
|
|
+ } else if(tuner_freq_MHz >= 1380) {
|
|
+ ucLoDiv1 = 3;
|
|
+ ucLomod1 = 16;
|
|
+ ucLoDiv2 = 2;
|
|
+ ucLomod2 = 0;
|
|
+ } else if(tuner_freq_MHz >= 1070) {
|
|
+ ucLoDiv1 = 3;
|
|
+ ucLomod1 = 16;
|
|
+ ucLoDiv2 = 3;
|
|
+ ucLomod2 = 16;
|
|
+ } else if(tuner_freq_MHz >= 1000) {
|
|
+ ucLoDiv1 = 3;
|
|
+ ucLomod1 = 16;
|
|
+ ucLoDiv2 = 4;
|
|
+ ucLomod2 = 64;
|
|
+ } else if(tuner_freq_MHz >= 775) {
|
|
+ ucLoDiv1 = 4;
|
|
+ ucLomod1 = 64;
|
|
+ ucLoDiv2 = 4;
|
|
+ ucLomod2 = 64;
|
|
+ } else if(tuner_freq_MHz >= 700) {
|
|
+ ucLoDiv1 = 6;
|
|
+ ucLomod1 = 48;
|
|
+ ucLoDiv2 = 4;
|
|
+ ucLomod2 = 64;
|
|
+ } else if(tuner_freq_MHz >= 520) {
|
|
+ ucLoDiv1 = 6;
|
|
+ ucLomod1 = 48;
|
|
+ ucLoDiv2 = 6;
|
|
+ ucLomod2 = 48;
|
|
+ } else {
|
|
+ ucLoDiv1 = 8;
|
|
+ ucLomod1 = 96;
|
|
+ ucLoDiv2 = 8;
|
|
+ ucLomod2 = 96;
|
|
+ }
|
|
+
|
|
+ ulNDiv1 = ((tuner_freq_MHz * ucLoDiv1 * 1000) * refDiv / fcry_KHz - 1024) / 2;
|
|
+ ulNDiv2 = ((tuner_freq_MHz * ucLoDiv2 * 1000) * refDiv / fcry_KHz - 1024) / 2;
|
|
+
|
|
+ reg27 = (((ulNDiv1 >> 8) & 0x0F) + ucLomod1) & 0x7F;
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x27, reg27);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x28, (u8)(ulNDiv1 & 0xFF));
|
|
+ reg29 = (((ulNDiv2 >> 8) & 0x0F) + ucLomod2) & 0x7f;
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x29, reg29);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x2a, (u8)(ulNDiv2 & 0xFF));
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x2F, 0xf5);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x30, 0x05);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x08, 0x1f);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x08, 0x3f);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x09, 0x20);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x09, 0x00);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x3e, 0x11);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x08, 0x2f);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x08, 0x3f);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x09, 0x10);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x09, 0x00);
|
|
+ msleep(2);
|
|
+
|
|
+ reg42 = dvbsky_m88rs6000_tuner_readreg(state, 0x42);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x3e, 0x10);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x08, 0x2f);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x08, 0x3f);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x09, 0x10);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x09, 0x00);
|
|
+ msleep(2);
|
|
+ reg42buf = dvbsky_m88rs6000_tuner_readreg(state, 0x42);
|
|
+ if(reg42buf < reg42)
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x3e, 0x11);
|
|
+ msleep(5);
|
|
+
|
|
+ reg2d = dvbsky_m88rs6000_tuner_readreg(state, 0x2d);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x2d, reg2d);
|
|
+
|
|
+ reg2e = dvbsky_m88rs6000_tuner_readreg(state, 0x2e);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x2e, reg2e);
|
|
+
|
|
+ reg27 = dvbsky_m88rs6000_tuner_readreg(state, 0x27);
|
|
+ reg27 = reg27 & 0x70;
|
|
+ reg83 = dvbsky_m88rs6000_tuner_readreg(state, 0x83);
|
|
+ reg83 = reg83 & 0x70;
|
|
+
|
|
+ if(reg27 == reg83) {
|
|
+ ucLoDiv = ucLoDiv1;
|
|
+ ulNDiv = ulNDiv1;
|
|
+ ucLomod = ucLomod1 / 16;
|
|
+ } else {
|
|
+ ucLoDiv = ucLoDiv2;
|
|
+ ulNDiv = ulNDiv2;
|
|
+ ucLomod = ucLomod2 / 16;
|
|
+ }
|
|
+
|
|
+ if ((ucLoDiv == 3) || (ucLoDiv == 6)) {
|
|
+ refDiv = 18;
|
|
+ reg36 = refDiv - 8;
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x36, reg36);
|
|
+ ulNDiv = ((tuner_freq_MHz * ucLoDiv * 1000) * refDiv / fcry_KHz - 1024) / 2;
|
|
+ }
|
|
+
|
|
+ reg27 = (0x80 + ((ucLomod << 4) & 0x70) + ((ulNDiv >> 8) & 0x0F)) & 0xFF;
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x27, reg27);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x28, (u8)(ulNDiv & 0xFF));
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x29, 0x80);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x31, 0x03);
|
|
+
|
|
+ if (ucLoDiv == 3) {
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x3b, 0xCE);
|
|
+ } else {
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x3b, 0x8A);
|
|
+ }
|
|
+
|
|
+ //tuner_lo_freq_KHz = fcry_KHz* (ulNDiv * 2 + 1024) / refDiv / ucLoDiv;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_tuner_set_bb(struct dvbsky_m88rs6000_state *state, u32 symbol_rate_KSs, s32 lpf_offset_KHz)
|
|
+{
|
|
+ u32 f3dB;
|
|
+ u8 reg40;
|
|
+
|
|
+ f3dB = symbol_rate_KSs * 9 / 14 + 2000;
|
|
+ f3dB += lpf_offset_KHz;
|
|
+ if(f3dB < 6000) f3dB = 6000;
|
|
+ if(f3dB > 43000) f3dB = 43000;
|
|
+ reg40 = f3dB / 1000;
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x40, reg40);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_set_carrier_offset(struct dvb_frontend *fe,
|
|
+ s32 carrier_offset_khz)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_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);
|
|
+ tmp = (2*tmp + state->iMclkKHz) / (2*state->iMclkKHz);
|
|
+
|
|
+ if (tmp < 0)
|
|
+ tmp += 65536;
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0x5f, tmp >> 8);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x5e, tmp & 0xff);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_set_symrate(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_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);
|
|
+ value = (((c->symbol_rate / 1000) << 15) + (state->iMclkKHz / 4)) / (state->iMclkKHz / 2);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x61, value & 0x00ff);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x62, (value & 0xff00) >> 8);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_set_CCI(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+ u8 tmp;
|
|
+
|
|
+ tmp = dvbsky_m88rs6000_readreg(state, 0x56);
|
|
+ tmp &= ~0x01;
|
|
+ dvbsky_m88rs6000_writereg(state, 0x56, tmp);
|
|
+
|
|
+ tmp = dvbsky_m88rs6000_readreg(state, 0x76);
|
|
+ tmp &= ~0x80;
|
|
+ dvbsky_m88rs6000_writereg(state, 0x76, tmp);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_init_reg(struct dvbsky_m88rs6000_state *state, const u8 *p_reg_tab, u32 size)
|
|
+{
|
|
+ u32 i;
|
|
+
|
|
+ for(i = 0; i < size; i+=2)
|
|
+ dvbsky_m88rs6000_writereg(state, p_reg_tab[i], p_reg_tab[i+1]);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_get_ts_mclk(struct dvbsky_m88rs6000_state *state, u32 *p_MCLK_KHz)
|
|
+{
|
|
+ u8 reg15, reg16, reg1D, reg1E, reg1F;
|
|
+ u8 sm, f0, f1, f2, f3, pll_ldpc_mode;
|
|
+ u16 pll_div_fb, N;
|
|
+ u32 MCLK_KHz;
|
|
+
|
|
+ *p_MCLK_KHz = MT_FE_MCLK_KHZ;
|
|
+
|
|
+ reg15 = dvbsky_m88rs6000_tuner_readreg(state, 0x15);
|
|
+ reg16 = dvbsky_m88rs6000_tuner_readreg(state, 0x16);
|
|
+ reg1D = dvbsky_m88rs6000_tuner_readreg(state, 0x1D);
|
|
+ reg1E = dvbsky_m88rs6000_tuner_readreg(state, 0x1E);
|
|
+ reg1F = dvbsky_m88rs6000_tuner_readreg(state, 0x1F);
|
|
+
|
|
+ pll_ldpc_mode = (reg15 >> 1) & 0x01;
|
|
+
|
|
+ MCLK_KHz = 9000;
|
|
+
|
|
+ pll_div_fb = reg15 & 0x01;
|
|
+ pll_div_fb <<= 8;
|
|
+ pll_div_fb += reg16;
|
|
+
|
|
+ MCLK_KHz *= (pll_div_fb + 32);
|
|
+
|
|
+ sm = reg1D & 0x03;
|
|
+
|
|
+ f3 = (reg1E >> 4) & 0x0F;
|
|
+ f2 = reg1E & 0x0F;
|
|
+ f1 = (reg1F >> 4) & 0x0F;
|
|
+ f0 = reg1F & 0x0F;
|
|
+
|
|
+ if(f3 == 0) f3 = 16;
|
|
+ if(f2 == 0) f2 = 16;
|
|
+ if(f1 == 0) f1 = 16;
|
|
+ if(f0 == 0) f0 = 16;
|
|
+
|
|
+ N = f2 + f1;
|
|
+
|
|
+ switch(sm) {
|
|
+ case 3:
|
|
+ N = f3 + f2 + f1 + f0;
|
|
+ break;
|
|
+ case 2:
|
|
+ N = f2 + f1 + f0;
|
|
+ break;
|
|
+ case 1:
|
|
+ case 0:
|
|
+ default:
|
|
+ N = f2 + f1;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ MCLK_KHz *= 4;
|
|
+ MCLK_KHz /= N;
|
|
+ *p_MCLK_KHz = MCLK_KHz;
|
|
+
|
|
+ dprintk("%s(), mclk=%d.\n", __func__, MCLK_KHz);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_set_ts_mclk(struct dvbsky_m88rs6000_state *state, u32 MCLK_KHz, u32 iSymRateKSs)
|
|
+{
|
|
+ u8 reg11 = 0x0A, reg15, reg16, reg1D, reg1E, reg1F, tmp;
|
|
+ u8 sm, f0 = 0, f1 = 0, f2 = 0, f3 = 0;
|
|
+ u16 pll_div_fb, N;
|
|
+ u32 div;
|
|
+
|
|
+ dprintk("%s(), mclk=%d, symbol rate=%d KSs.\n", __func__, MCLK_KHz, iSymRateKSs);
|
|
+
|
|
+ reg15 = dvbsky_m88rs6000_tuner_readreg(state, 0x15);
|
|
+ reg16 = dvbsky_m88rs6000_tuner_readreg(state, 0x16);
|
|
+ reg1D = dvbsky_m88rs6000_tuner_readreg(state, 0x1D);
|
|
+
|
|
+ if(reg16 == 92)
|
|
+ tmp = 93;
|
|
+ else if (reg16 == 100)
|
|
+ tmp = 99;
|
|
+ else
|
|
+ tmp = 96;
|
|
+ MCLK_KHz *= tmp;
|
|
+ MCLK_KHz /= 96;
|
|
+
|
|
+ pll_div_fb = (reg15 & 0x01) << 8;
|
|
+ pll_div_fb += reg16;
|
|
+ pll_div_fb += 32;
|
|
+
|
|
+ div = 9000 * pll_div_fb * 4;
|
|
+ div /= MCLK_KHz;
|
|
+
|
|
+ if(state->config->ts_mode == 1) {
|
|
+ reg11 |= 0x02;
|
|
+
|
|
+ if(div <= 32) {
|
|
+ N = 2;
|
|
+ f0 = 0;
|
|
+ f1 = div / N;
|
|
+ f2 = div - f1;
|
|
+ f3 = 0;
|
|
+ } else if (div <= 34) {
|
|
+ N = 3;
|
|
+ f0 = div / N;
|
|
+ f1 = (div - f0) / (N - 1);
|
|
+ f2 = div - f0 - f1;
|
|
+ f3 = 0;
|
|
+ } else if (div <= 64) {
|
|
+ N = 4;
|
|
+ f0 = div / N;
|
|
+ f1 = (div - f0) / (N - 1);
|
|
+ f2 = (div - f0 - f1) / (N - 2);
|
|
+ f3 = div - f0 - f1 - f2;
|
|
+ } else {
|
|
+ N = 4;
|
|
+ f0 = 16;
|
|
+ f1 = 16;
|
|
+ f2 = 16;
|
|
+ f3 = 16;
|
|
+ }
|
|
+
|
|
+ if(f0 == 16)
|
|
+ f0 = 0;
|
|
+ else if((f0 < 8) && (f0 != 0))
|
|
+ f0 = 8;
|
|
+
|
|
+ if(f1 == 16)
|
|
+ f1 = 0;
|
|
+ else if((f1 < 8) && (f1 != 0))
|
|
+ f1 = 8;
|
|
+
|
|
+ if(f2 == 16)
|
|
+ f2 = 0;
|
|
+ else if((f2 < 8) && (f2 != 0))
|
|
+ f2 = 8;
|
|
+
|
|
+ if(f3 == 16)
|
|
+ f3 = 0;
|
|
+ else if((f3 < 8) && (f3 != 0))
|
|
+ f3 = 8;
|
|
+ } else {
|
|
+ reg11 &= ~0x02;
|
|
+ if(div <= 32) {
|
|
+ N = 2;
|
|
+ f0 = 0;
|
|
+ f1 = div / N;
|
|
+ f2 = div - f1;
|
|
+ f3 = 0;
|
|
+ } else if(div <= 48) {
|
|
+ N = 3;
|
|
+ f0 = div / N;
|
|
+ f1 = (div - f0) / (N - 1);
|
|
+ f2 = div - f0 - f1;
|
|
+ f3 = 0;
|
|
+ } else if(div <= 64) {
|
|
+ N = 4;
|
|
+ f0 = div / N;
|
|
+ f1 = (div - f0) / (N - 1);
|
|
+ f2 = (div - f0 - f1) / (N - 2);
|
|
+ f3 = div - f0 - f1 - f2;
|
|
+ } else {
|
|
+ N = 4;
|
|
+ f0 = 16;
|
|
+ f1 = 16;
|
|
+ f2 = 16;
|
|
+ f3 = 16;
|
|
+ }
|
|
+
|
|
+ if(f0 == 16)
|
|
+ f0 = 0;
|
|
+ else if((f0 < 9) && (f0 != 0))
|
|
+ f0 = 9;
|
|
+
|
|
+ if(f1 == 16)
|
|
+ f1 = 0;
|
|
+ else if((f1 < 9) && (f1 != 0))
|
|
+ f1 = 9;
|
|
+
|
|
+ if(f2 == 16)
|
|
+ f2 = 0;
|
|
+ else if((f2 < 9) && (f2 != 0))
|
|
+ f2 = 9;
|
|
+
|
|
+ if(f3 == 16)
|
|
+ f3 = 0;
|
|
+ else if((f3 < 9) && (f3 != 0))
|
|
+ f3 = 9;
|
|
+ }
|
|
+
|
|
+ sm = N - 1;
|
|
+ reg1D &= ~0x03;
|
|
+ reg1D |= sm;
|
|
+ reg1E = ((f3 << 4) + f2) & 0xFF;
|
|
+ reg1F = ((f1 << 4) + f0) & 0xFF;
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x05, 0x40);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x11, 0x08);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x1D, reg1D);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x1E, reg1E);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x1F, reg1F);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x17, 0xc1);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x17, 0x81);
|
|
+ msleep(5);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x05, 0x00);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x11, (iSymRateKSs > 45010) ? 0x0E : 0x0A);
|
|
+ msleep(5);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_set_ts_divide_ratio(struct dvbsky_m88rs6000_state *state, u8 dr_high, u8 dr_low)
|
|
+{
|
|
+ u8 val, tmp1, tmp2;
|
|
+
|
|
+ tmp1 = dr_high;
|
|
+ tmp2 = dr_low;
|
|
+
|
|
+ tmp1 -= 1;
|
|
+ tmp2 -= 1;
|
|
+
|
|
+ tmp1 &= 0x3f;
|
|
+ tmp2 &= 0x3f;
|
|
+
|
|
+ val = dvbsky_m88rs6000_readreg(state, 0xfe);
|
|
+ val &= 0xF0;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xfe, val);
|
|
+
|
|
+ val = (u8)((tmp1 & 0x03) << 6);
|
|
+ val |= tmp2;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xea, val);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_demod_connect(struct dvb_frontend *fe, s32 carrier_offset_khz)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
+
|
|
+ u8 tmp, tmp1, tmp2;
|
|
+ u16 divide_ratio;
|
|
+ u32 target_mclk = MT_FE_MCLK_KHZ, ts_clk;
|
|
+
|
|
+ dprintk("connect delivery system = %d\n", state->delivery_system);
|
|
+
|
|
+ /* rs6000 build-in uC reset */
|
|
+ dvbsky_m88rs6000_writereg(state, 0xb2, 0x01);
|
|
+ /* rs6000 software reset */
|
|
+ dvbsky_m88rs6000_writereg(state, 0x00, 0x01);
|
|
+
|
|
+ switch (state->delivery_system) {
|
|
+ case SYS_DVBS:
|
|
+ /* initialise the demod in DVB-S mode */
|
|
+ dvbsky_m88rs6000_init_reg(state, rs6000_dvbs_init_tab, sizeof(rs6000_dvbs_init_tab));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x4d, 0xfd & dvbsky_m88rs6000_readreg(state, 0x4d));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x30, 0xef & dvbsky_m88rs6000_readreg(state, 0x30));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x29, 0x10 | dvbsky_m88rs6000_readreg(state, 0x29));
|
|
+
|
|
+ target_mclk = 96000;
|
|
+ break;
|
|
+ case SYS_DVBS2:
|
|
+ dvbsky_m88rs6000_init_reg(state, rs6000_dvbs2_init_tab, sizeof(rs6000_dvbs2_init_tab));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x4d, 0xfd & dvbsky_m88rs6000_readreg(state, 0x4d));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x30, 0xef & dvbsky_m88rs6000_readreg(state, 0x30));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x29, 0xef & dvbsky_m88rs6000_readreg(state, 0x29));
|
|
+
|
|
+ if(state->config->ts_mode == 1) {
|
|
+ target_mclk = 96000;
|
|
+ } else {
|
|
+ target_mclk = 144000;
|
|
+ }
|
|
+
|
|
+ if((c->symbol_rate / 1000 ) <= 5000) {
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc0, 0x04);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x8a, 0x09);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x8b, 0x22);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x8c, 0x88);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ /* set ts clock */
|
|
+ if(state->config->ci_mode == 0)
|
|
+ ts_clk = 16000;
|
|
+ else
|
|
+ ts_clk = 8000;
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0x06, 0xe0);
|
|
+ dvbsky_m88rs6000_set_ts_mclk(state, target_mclk, c->symbol_rate / 1000);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x06, 0x00);
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0x9d, 0x08 | dvbsky_m88rs6000_readreg(state, 0x9d));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x30, 0x80 | dvbsky_m88rs6000_readreg(state, 0x30));
|
|
+
|
|
+ dvbsky_m88rs6000_get_ts_mclk(state, &target_mclk);
|
|
+
|
|
+ divide_ratio = (target_mclk + ts_clk - 1) / ts_clk;
|
|
+ if(divide_ratio > 128)
|
|
+ divide_ratio = 128;
|
|
+ if(divide_ratio < 2)
|
|
+ divide_ratio = 2;
|
|
+ tmp1 = (u8)(divide_ratio / 2);
|
|
+ tmp2 = (u8)(divide_ratio / 2);
|
|
+ if((divide_ratio % 2) != 0)
|
|
+ tmp2 += 1;
|
|
+
|
|
+ dvbsky_m88rs6000_set_ts_divide_ratio(state, tmp1, tmp2);
|
|
+
|
|
+ /* set ts pins.*/
|
|
+ if(state->config->ci_mode){
|
|
+ if(state->config->ci_mode == 2)
|
|
+ tmp = 0x43;
|
|
+ else
|
|
+ tmp = 0x03;
|
|
+ }else if(state->config->ts_mode)
|
|
+ tmp = 0x06;
|
|
+ else
|
|
+ tmp = 0x42;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xfd, tmp);
|
|
+
|
|
+ /* set others.*/
|
|
+ tmp = dvbsky_m88rs6000_readreg(state, 0xca);
|
|
+ tmp &= 0xFE;
|
|
+ tmp |= (dvbsky_m88rs6000_readreg(state, 0xfd) >> 3) & 0x01;
|
|
+ dvbsky_m88rs6000_writereg(state, 0xca, tmp);
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0x33, 0x99);
|
|
+
|
|
+ /* enable ac coupling */
|
|
+ dvbsky_m88rs6000_writereg(state, 0x25, 0x08 | dvbsky_m88rs6000_readreg(state, 0x25));
|
|
+ dvbsky_m88rs6000_writereg(state, 0xC9, 0x08 | dvbsky_m88rs6000_readreg(state, 0xC9));
|
|
+
|
|
+ if ((c->symbol_rate / 1000) <= 3000){
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc3, 0x08); /* 8 * 32 * 100 / 64 = 400*/
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc8, 0x20);
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc7, 0x00);
|
|
+ }else if((c->symbol_rate / 1000) <= 10000){
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc3, 0x08); /* 8 * 16 * 100 / 64 = 200*/
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc8, 0x10);
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc7, 0x00);
|
|
+ }else{
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc3, 0x08); /* 8 * 6 * 100 / 64 = 75*/
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc8, 0x06);
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc4, 0x08); /* 8 * 0 * 100 / 128 = 0*/
|
|
+ dvbsky_m88rs6000_writereg(state, 0xc7, 0x00);
|
|
+ }
|
|
+
|
|
+ dvbsky_m88rs6000_set_symrate(fe);
|
|
+
|
|
+ dvbsky_m88rs6000_set_CCI(fe);
|
|
+
|
|
+ dvbsky_m88rs6000_set_carrier_offset(fe, carrier_offset_khz);
|
|
+
|
|
+ /* rs6000 out of software reset */
|
|
+ dvbsky_m88rs6000_writereg(state, 0x00, 0x00);
|
|
+ /* start rs6000 build-in uC */
|
|
+ dvbsky_m88rs6000_writereg(state, 0xb2, 0x00);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_select_mclk(struct dvbsky_m88rs6000_state *state, u32 tuner_freq_MHz, u32 iSymRateKSs)
|
|
+{
|
|
+ u32 adc_Freq_MHz[3] = {96, 93, 99};
|
|
+ u8 reg16_list[3] = {96, 92, 100}, reg16, reg15;
|
|
+ u32 offset_MHz[3];
|
|
+ u32 max_offset = 0;
|
|
+ u8 i;
|
|
+
|
|
+ if(iSymRateKSs > 45010) {
|
|
+ reg16 = 115;
|
|
+ state->iMclkKHz = 110250;
|
|
+ } else {
|
|
+ adc_Freq_MHz[0] = 96;
|
|
+ adc_Freq_MHz[1] = 93;
|
|
+ adc_Freq_MHz[2] = 99;
|
|
+ reg16_list[0] = 96;
|
|
+ reg16_list[1] = 92;
|
|
+ reg16_list[2] = 100;
|
|
+ reg16 = 96;
|
|
+ for(i = 0; i < 3; i++) {
|
|
+ offset_MHz[i] = tuner_freq_MHz % adc_Freq_MHz[i];
|
|
+
|
|
+ if(offset_MHz[i] > (adc_Freq_MHz[i] / 2))
|
|
+ offset_MHz[i] = adc_Freq_MHz[i] - offset_MHz[i];
|
|
+
|
|
+ if(offset_MHz[i] > max_offset) {
|
|
+ max_offset = offset_MHz[i];
|
|
+ reg16 = reg16_list[i];
|
|
+ state->iMclkKHz = adc_Freq_MHz[i] * 1000;
|
|
+
|
|
+ if(iSymRateKSs > 45010)
|
|
+ state->iMclkKHz /= 2;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ reg15 = dvbsky_m88rs6000_tuner_readreg(state, 0x15);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x05, 0x40);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x11, 0x08);
|
|
+ if(iSymRateKSs > 45010)
|
|
+ reg15 |= 0x02;
|
|
+ else
|
|
+ reg15 &= ~0x02;
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x15, reg15);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x16, reg16);
|
|
+ msleep(5);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x05, 0x00);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x11, (iSymRateKSs > 45010) ? 0x0E : 0x0A);
|
|
+ msleep(5);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_set_frontend(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
|
|
+
|
|
+ int i;
|
|
+ u32 target_mclk = 144000;
|
|
+ s32 lpf_offset_KHz;
|
|
+ u32 realFreq, freq_MHz;
|
|
+ fe_status_t status;
|
|
+
|
|
+ dprintk("%s() ", __func__);
|
|
+ dprintk("c frequency = %d KHz\n", c->frequency);
|
|
+ dprintk("symbol rate = %d\n", c->symbol_rate);
|
|
+ dprintk("delivery system = %d\n", c->delivery_system);
|
|
+
|
|
+ state->delivery_system = c->delivery_system;
|
|
+ if( state->delivery_system == SYS_DVBS )
|
|
+ target_mclk = 96000;
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ /* set mclk.*/
|
|
+ dvbsky_m88rs6000_writereg(state, 0x06, 0xe0);
|
|
+ dvbsky_m88rs6000_select_mclk(state, realFreq / 1000, c->symbol_rate / 1000);
|
|
+ dvbsky_m88rs6000_set_ts_mclk(state, target_mclk, c->symbol_rate / 1000);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x06, 0x00);
|
|
+ msleep(10);
|
|
+
|
|
+ /* set tuner pll.*/
|
|
+ freq_MHz = (realFreq + 500) / 1000;
|
|
+ dvbsky_m88rs6000_tuner_set_pll_freq(state, freq_MHz);
|
|
+ dvbsky_m88rs6000_tuner_set_bb(state, c->symbol_rate / 1000, lpf_offset_KHz);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x00, 0x01);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x00, 0x00);
|
|
+
|
|
+ /* start demod to lock */
|
|
+ dvbsky_m88rs6000_demod_connect(fe, lpf_offset_KHz);
|
|
+
|
|
+ /* check lock status.*/
|
|
+ for (i = 0; i < 30 ; i++) {
|
|
+ dvbsky_m88rs6000_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;
|
|
+ dvbsky_m88rs6000_demod_connect(fe, lpf_offset_KHz);
|
|
+
|
|
+ for (i = 0; i < 30 ; i++) {
|
|
+ dvbsky_m88rs6000_read_status(fe, &status);
|
|
+ if (status & FE_HAS_LOCK)
|
|
+ break;
|
|
+ msleep(20);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (status & FE_HAS_LOCK){
|
|
+ if (state->config->set_ts_params)
|
|
+ state->config->set_ts_params(fe, 0);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int dvbsky_m88rs6000_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 = dvbsky_m88rs6000_set_frontend(fe);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return dvbsky_m88rs6000_read_status(fe, status);
|
|
+}
|
|
+
|
|
+static enum dvbfe_algo dvbsky_m88rs6000_get_algo(struct dvb_frontend *fe)
|
|
+{
|
|
+ return DVBFE_ALGO_HW;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialise or wake up device
|
|
+ */
|
|
+static int dvbsky_m88rs6000_initfe(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+
|
|
+ dprintk("%s()\n", __func__);
|
|
+
|
|
+ /* 1st step to wake up demod */
|
|
+ dvbsky_m88rs6000_writereg(state, 0x04, 0xfe & dvbsky_m88rs6000_readreg(state, 0x04));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x23, 0xef & dvbsky_m88rs6000_readreg(state, 0x23));
|
|
+
|
|
+ /* 2nd step to wake up tuner */
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x11, 0x08 | dvbsky_m88rs6000_tuner_readreg(state, 0x11));
|
|
+ msleep(5);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x10, 0x01 | dvbsky_m88rs6000_tuner_readreg(state, 0x10));
|
|
+ msleep(10);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x07, 0x7d);
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0x08, 0x01 | dvbsky_m88rs6000_readreg(state, 0x08));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x29, 0x01 | dvbsky_m88rs6000_readreg(state, 0x29));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Put device to sleep */
|
|
+static int dvbsky_m88rs6000_sleep(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+
|
|
+ dprintk("%s()\n", __func__);
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0x29, 0xfe & dvbsky_m88rs6000_readreg(state, 0x29));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x08, 0xfe & dvbsky_m88rs6000_readreg(state, 0x08));
|
|
+
|
|
+ /* 1st step to sleep tuner */
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x07, 0x6d);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x10, 0xfe & dvbsky_m88rs6000_tuner_readreg(state, 0x10));
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x11, 0xf7 & dvbsky_m88rs6000_tuner_readreg(state, 0x11));
|
|
+ msleep(5);
|
|
+
|
|
+ /* 2nd step to sleep demod */
|
|
+ dvbsky_m88rs6000_writereg(state, 0x04, 0x01 | dvbsky_m88rs6000_readreg(state, 0x04));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x23, 0x10 | dvbsky_m88rs6000_readreg(state, 0x23));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+ /*
|
|
+ * Power config will reset and load initial firmware if required
|
|
+ */
|
|
+static int dvbsky_m88rs6000_initilaze(struct dvb_frontend *fe)
|
|
+{
|
|
+ struct dvbsky_m88rs6000_state *state = fe->demodulator_priv;
|
|
+ int ret;
|
|
+ u8 val;
|
|
+
|
|
+ dprintk("%s()\n", __func__);
|
|
+
|
|
+ val = dvbsky_m88rs6000_readreg(state, 0x29);
|
|
+ state->tuner_addr = ( val & 0x80) ? 0x20 : 0x21;
|
|
+
|
|
+ dvbsky_m88rs6000_initfe(fe);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x04, 0x01);
|
|
+
|
|
+ if(dvbsky_m88rs6000_check_id(state) != RS6000_ID)
|
|
+ return 1;
|
|
+
|
|
+ /* hard reset */
|
|
+ val = dvbsky_m88rs6000_readreg(state, 0x08);
|
|
+ val &= 0xfe;
|
|
+ dvbsky_m88rs6000_writereg(state, 0x08, val);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x07, 0x80);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x07, 0x00);
|
|
+ dvbsky_m88rs6000_writereg(state, 0xb2, 0x00);
|
|
+ val |= 0x01;
|
|
+ dvbsky_m88rs6000_writereg(state, 0x08, val);
|
|
+ msleep(1);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x08, 0x01 | dvbsky_m88rs6000_readreg(state, 0x08));
|
|
+ msleep(1);
|
|
+
|
|
+ /* tuner init. */
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x05, 0x40);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x11, 0x08);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x15, 0x6c);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x17, 0xc1);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x17, 0x81);
|
|
+ msleep(10);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x05, 0x00);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x11, 0x0a);
|
|
+
|
|
+ /* set tuner to normal state.*/
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x11, 0x08 | dvbsky_m88rs6000_tuner_readreg(state, 0x11));
|
|
+ msleep(5);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x10, 0x01 | dvbsky_m88rs6000_tuner_readreg(state, 0x10));
|
|
+ msleep(10);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x07, 0x7d);
|
|
+
|
|
+ /*disable tuner clock output.*/
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x10, 0xfb);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x24, 0x38);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x11, 0x0a);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x12, 0x00);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x2b, 0x1c);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x44, 0x48);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x54, 0x24);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x55, 0x06);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x59, 0x00);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x5b, 0x4c);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x60, 0x8b);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x61, 0xf4);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x65, 0x07);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x6d, 0x6f);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x6e, 0x31);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x3c, 0xf3);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x37, 0x0f);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x48, 0x28);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x49, 0xd8);
|
|
+
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x70, 0x66);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x71, 0xCF);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x72, 0x81);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x73, 0xA7);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x74, 0x4F);
|
|
+ dvbsky_m88rs6000_tuner_writereg(state, 0x75, 0xFC);
|
|
+
|
|
+ /* demod reset.*/
|
|
+ dvbsky_m88rs6000_writereg(state, 0x07, 0xE0);
|
|
+ dvbsky_m88rs6000_writereg(state, 0x07, 0x00);
|
|
+
|
|
+ /* Load the firmware if required */
|
|
+ ret = dvbsky_m88rs6000_load_firmware(fe);
|
|
+ if (ret != 0){
|
|
+ printk(KERN_ERR "%s: Unable download firmware\n", __func__);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0x4d, 0xfd & dvbsky_m88rs6000_readreg(state, 0x4d));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x30, 0xef & dvbsky_m88rs6000_readreg(state, 0x30));
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0xf1, 0x01);
|
|
+
|
|
+ dvbsky_m88rs6000_writereg(state, 0x29, 0xbf & dvbsky_m88rs6000_readreg(state, 0x29));
|
|
+ dvbsky_m88rs6000_writereg(state, 0x9d, 0x08 | dvbsky_m88rs6000_readreg(state, 0x9d));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct dvb_frontend_ops dvbsky_m88rs6000_ops = {
|
|
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
|
|
+ .info = {
|
|
+ .name = "Montage RS6000(DVBSky)",
|
|
+ .type = FE_QPSK,
|
|
+ .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 = dvbsky_m88rs6000_release,
|
|
+ .init = dvbsky_m88rs6000_initfe,
|
|
+ .sleep = dvbsky_m88rs6000_sleep,
|
|
+ .read_status = dvbsky_m88rs6000_read_status,
|
|
+ .read_ber = dvbsky_m88rs6000_read_ber,
|
|
+ .read_signal_strength = dvbsky_m88rs6000_read_signal_strength,
|
|
+ .read_snr = dvbsky_m88rs6000_read_snr,
|
|
+ .read_ucblocks = dvbsky_m88rs6000_read_ucblocks,
|
|
+ .set_tone = dvbsky_m88rs6000_set_tone,
|
|
+ .set_voltage = dvbsky_m88rs6000_set_voltage,
|
|
+ .diseqc_send_master_cmd = dvbsky_m88rs6000_send_diseqc_msg,
|
|
+ .diseqc_send_burst = dvbsky_m88rs6000_diseqc_send_burst,
|
|
+ .get_frontend_algo = dvbsky_m88rs6000_get_algo,
|
|
+ .tune = dvbsky_m88rs6000_tune,
|
|
+ .set_frontend = dvbsky_m88rs6000_set_frontend,
|
|
+};
|
|
+
|
|
+MODULE_DESCRIPTION("DVB Frontend module for Montage M88RS6000");
|
|
+MODULE_AUTHOR("Max nibble");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/drivers/media/dvb-frontends/dvbsky_m88rs6000.h b/drivers/media/dvb-frontends/dvbsky_m88rs6000.h
|
|
new file mode 100644
|
|
index 0000000..d790dad
|
|
--- /dev/null
|
|
+++ b/drivers/media/dvb-frontends/dvbsky_m88rs6000.h
|
|
@@ -0,0 +1,39 @@
|
|
+/*
|
|
+ Montage Technology M88RS6000
|
|
+ - DVBS/S2 Satellite demod/tuner driver
|
|
+ Copyright (C) 2014 Max Nibble <nibble.max@gmail.com>
|
|
+
|
|
+ */
|
|
+
|
|
+#ifndef DVBSKY_M88RS6000_H
|
|
+#define DVBSKY_M88RS6000_H
|
|
+
|
|
+#include <linux/kconfig.h>
|
|
+#include <linux/dvb/frontend.h>
|
|
+
|
|
+struct dvbsky_m88rs6000_config {
|
|
+ u8 demod_address; /* the demodulator's i2c address */
|
|
+ u8 pin_ctrl; /* LNB pin control.*/
|
|
+ u8 ci_mode; /*0: no ci, others: ci mode.*/
|
|
+ u8 ts_mode; /* 0: Parallel, 1: Serial */
|
|
+ u8 tuner_readstops;
|
|
+ /* Set device param to start dma */
|
|
+ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
|
|
+ /* Set LNB voltage */
|
|
+ int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
|
|
+};
|
|
+
|
|
+#if IS_ENABLED(CONFIG_DVB_DVBSKY_M88RS6000)
|
|
+extern struct dvb_frontend *dvbsky_m88rs6000_attach(
|
|
+ const struct dvbsky_m88rs6000_config *config,
|
|
+ struct i2c_adapter *i2c);
|
|
+#else
|
|
+static inline struct dvb_frontend *dvbsky_m88rs6000_attach(
|
|
+ const struct dvbsky_m88rs6000_config *config,
|
|
+ struct i2c_adapter *i2c)
|
|
+{
|
|
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
|
+ return NULL;
|
|
+}
|
|
+#endif /* CONFIG_DVB_DVBSKY_M88RS6000 */
|
|
+#endif /* DVBSKY_M88RS6000_H */
|
|
diff --git a/drivers/media/dvb-frontends/dvbsky_m88rs6000_priv.h b/drivers/media/dvb-frontends/dvbsky_m88rs6000_priv.h
|
|
new file mode 100644
|
|
index 0000000..4a6a489
|
|
--- /dev/null
|
|
+++ b/drivers/media/dvb-frontends/dvbsky_m88rs6000_priv.h
|
|
@@ -0,0 +1,210 @@
|
|
+/*
|
|
+ Montage Technology M88RS6000
|
|
+ - DVBS/S2 Satellite demod/tuner driver
|
|
+ Copyright (C) 2014 Max Nibble <nibble.max@gmail.com>
|
|
+
|
|
+ */
|
|
+
|
|
+#ifndef DVBSKY_M88RS6000_PRIV_H
|
|
+#define DVBSKY_M88RS6000_PRIV_H
|
|
+
|
|
+#define FW_DOWN_SIZE 32
|
|
+#define FW_DOWN_LOOP (8192/FW_DOWN_SIZE)
|
|
+#define RS6000_DEFAULT_FIRMWARE "dvb-fe-rs6000.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 RS6000_ID 0x6000
|
|
+
|
|
+struct dvbsky_m88rs6000_state {
|
|
+ struct i2c_adapter *i2c;
|
|
+ const struct dvbsky_m88rs6000_config *config;
|
|
+
|
|
+ struct dvb_frontend frontend;
|
|
+
|
|
+ u8 tuner_addr;
|
|
+ u32 preBer;
|
|
+ u8 skip_fw_load;
|
|
+ u8 first_lock; /* The first time of signal lock */
|
|
+ u16 demod_id; /* demod chip type */
|
|
+ u16 tuner_id; /* tuner chip type */
|
|
+ fe_delivery_system_t delivery_system;
|
|
+ u32 iMclkKHz;
|
|
+};
|
|
+
|
|
+/* For M88RS6000 demod dvbs mode.*/
|
|
+static u8 rs6000_dvbs_init_tab[] = {
|
|
+ 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,
|
|
+};
|
|
+
|
|
+/* For M88RS6000 demod dvbs2 mode.*/
|
|
+static u8 rs6000_dvbs2_init_tab[] = {
|
|
+ 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,
|
|
+};
|
|
+
|
|
+#endif /* DVBSKY_M88RS6000_PRIV_H */
|
|
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
|
|
index 53196f1..e7d2bb0 100644
|
|
--- a/drivers/media/pci/Kconfig
|
|
+++ b/drivers/media/pci/Kconfig
|
|
@@ -43,6 +43,7 @@ source "drivers/media/pci/pt1/Kconfig"
|
|
source "drivers/media/pci/mantis/Kconfig"
|
|
source "drivers/media/pci/ngene/Kconfig"
|
|
source "drivers/media/pci/ddbridge/Kconfig"
|
|
+source "drivers/media/pci/smipcie/Kconfig"
|
|
endif
|
|
|
|
endif #MEDIA_PCI_SUPPORT
|
|
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
|
|
index 35cc578..af2f701 100644
|
|
--- a/drivers/media/pci/Makefile
|
|
+++ b/drivers/media/pci/Makefile
|
|
@@ -10,6 +10,7 @@ obj-y += ttpci/ \
|
|
mantis/ \
|
|
ngene/ \
|
|
ddbridge/ \
|
|
+ smipcie/ \
|
|
b2c2/ \
|
|
saa7146/
|
|
|
|
diff --git a/drivers/media/pci/smipcie/Kconfig b/drivers/media/pci/smipcie/Kconfig
|
|
new file mode 100644
|
|
index 0000000..341917d
|
|
--- /dev/null
|
|
+++ b/drivers/media/pci/smipcie/Kconfig
|
|
@@ -0,0 +1,12 @@
|
|
+config DVB_SMIPCIE
|
|
+ tristate "SMI PCIe DVBSky cards"
|
|
+ depends on DVB_CORE && PCI && I2C
|
|
+ select DVB_DVBSKY_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
|
|
+ select DVB_DVBSKY_M88RS6000 if MEDIA_SUBDRV_AUTOSELECT
|
|
+ select DVB_SIT2 if MEDIA_SUBDRV_AUTOSELECT
|
|
+ depends on RC_CORE
|
|
+ ---help---
|
|
+ Support for cards from DVBSky.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called smipcie
|
|
diff --git a/drivers/media/pci/smipcie/Makefile b/drivers/media/pci/smipcie/Makefile
|
|
new file mode 100644
|
|
index 0000000..20af47a
|
|
--- /dev/null
|
|
+++ b/drivers/media/pci/smipcie/Makefile
|
|
@@ -0,0 +1,3 @@
|
|
+obj-$(CONFIG_DVB_SMIPCIE) += smipcie.o
|
|
+
|
|
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends
|
|
diff --git a/drivers/media/pci/smipcie/smipcie.c b/drivers/media/pci/smipcie/smipcie.c
|
|
new file mode 100644
|
|
index 0000000..f4777f2
|
|
--- /dev/null
|
|
+++ b/drivers/media/pci/smipcie/smipcie.c
|
|
@@ -0,0 +1,1131 @@
|
|
+/*
|
|
+ SMI PCIe driver for DVBSky cards.
|
|
+ Copyright (C) 2014 Max Nibble <nibble.max@gmail.com>
|
|
+
|
|
+*/
|
|
+#include "smipcie.h"
|
|
+#include "dvbsky_m88rs6000.h"
|
|
+#include "dvbsky_m88ds3103.h"
|
|
+
|
|
+static int debug;
|
|
+module_param(debug, int, 0644);
|
|
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
|
|
+
|
|
+#define dprintk(args...) \
|
|
+ do { \
|
|
+ if (debug) \
|
|
+ printk(KERN_INFO "smi_pcie: " args); \
|
|
+ } while (0)
|
|
+
|
|
+
|
|
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
|
+
|
|
+static int smi_hw_init(struct smi_dev *dev)
|
|
+{
|
|
+ u32 port_mux, port_ctrl, int_stat;
|
|
+ dprintk("%s\n", __func__);
|
|
+
|
|
+ /* set port mux.*/
|
|
+ port_mux = smi_read(MUX_MODE_CTRL);
|
|
+ port_mux &= ~(rbPaMSMask);
|
|
+ port_mux |= rbPaMSDtvNoGpio;
|
|
+ port_mux &= ~(rbPbMSMask);
|
|
+ port_mux |= rbPbMSDtvNoGpio;
|
|
+ port_mux &= ~(0x0f0000);
|
|
+ port_mux |= 0x50000;
|
|
+ smi_write(MUX_MODE_CTRL, port_mux);
|
|
+
|
|
+ /* set DTV register.*/
|
|
+ /* Port A */
|
|
+ port_ctrl = smi_read(VIDEO_CTRL_STATUS_A);
|
|
+ port_ctrl &= ~0x01;
|
|
+ smi_write(VIDEO_CTRL_STATUS_A, port_ctrl);
|
|
+ port_ctrl = smi_read(MPEG2_CTRL_A);
|
|
+ port_ctrl &= ~0x40;
|
|
+ port_ctrl |= 0x80;
|
|
+ smi_write(MPEG2_CTRL_A, port_ctrl);
|
|
+ /* Port B */
|
|
+ port_ctrl = smi_read(VIDEO_CTRL_STATUS_B);
|
|
+ port_ctrl &= ~0x01;
|
|
+ smi_write(VIDEO_CTRL_STATUS_B, port_ctrl);
|
|
+ port_ctrl = smi_read(MPEG2_CTRL_B);
|
|
+ port_ctrl &= ~0x40;
|
|
+ port_ctrl |= 0x80;
|
|
+ smi_write(MPEG2_CTRL_B, port_ctrl);
|
|
+
|
|
+ /* disable and clear interrupt.*/
|
|
+ smi_write(MSI_INT_ENA_CLR, ALL_INT);
|
|
+ int_stat = smi_read(MSI_INT_STATUS);
|
|
+ smi_write(MSI_INT_STATUS_CLR, int_stat);
|
|
+
|
|
+ /* reset demod.*/
|
|
+ smi_clear(PERIPHERAL_CTRL, 0x0303);
|
|
+ msleep(50);
|
|
+ smi_set(PERIPHERAL_CTRL, 0x0101);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void smi_ir_enableInterrupt(struct smi_rc *ir)
|
|
+{
|
|
+ struct smi_dev *dev = ir->dev;
|
|
+ smi_write(MSI_INT_ENA_SET, IR_X_INT);
|
|
+}
|
|
+static void smi_ir_disableInterrupt(struct smi_rc *ir)
|
|
+{
|
|
+ struct smi_dev *dev = ir->dev;
|
|
+ smi_write(MSI_INT_ENA_CLR, IR_X_INT);
|
|
+}
|
|
+static void smi_ir_clearInterrupt(struct smi_rc *ir)
|
|
+{
|
|
+ struct smi_dev *dev = ir->dev;
|
|
+ smi_write(MSI_INT_STATUS_CLR, IR_X_INT);
|
|
+}
|
|
+static void smi_ir_start(struct smi_rc *ir)
|
|
+{
|
|
+ struct smi_dev *dev = ir->dev;
|
|
+
|
|
+ smi_write(IR_Idle_Cnt_Low, 0x00140070);
|
|
+ /* smi_write(IR_Idle_Cnt_Low, 0x003200c8); */
|
|
+ msleep(2);
|
|
+ smi_set(IR_Init_Reg, 0x90);
|
|
+
|
|
+ smi_ir_enableInterrupt(ir);
|
|
+ /* tasklet_enable(&ir->tasklet);*/
|
|
+}
|
|
+static void smi_ir_stop(struct smi_rc *ir)
|
|
+{
|
|
+ struct smi_dev *dev = ir->dev;
|
|
+
|
|
+ /* tasklet_disable(&ir->tasklet);*/
|
|
+ smi_ir_disableInterrupt(ir);
|
|
+ smi_clear(IR_Init_Reg, 0x80);
|
|
+}
|
|
+/*
|
|
+static int smi_ir_open(struct rc_dev *rc)
|
|
+{
|
|
+ struct smi_rc *ir = rc->priv;
|
|
+ if(!ir)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if(ir->users++ == 0)
|
|
+ smi_ir_start(ir);
|
|
+ return 0;
|
|
+}
|
|
+static void smi_ir_close(struct rc_dev *rc)
|
|
+{
|
|
+ struct smi_rc *ir = rc->priv;
|
|
+ if(ir) {
|
|
+ --ir->users;
|
|
+ if(!ir->users)
|
|
+ smi_ir_stop(ir);
|
|
+ }
|
|
+}
|
|
+*/
|
|
+
|
|
+#define BITS_PER_COMMAND 14
|
|
+#define GROUPS_PER_BIT 2
|
|
+#define IR_RC5_MIN_BIT 36
|
|
+#define IR_RC5_MAX_BIT 52
|
|
+static u32 smi_decode_rc5(u8 *pData, u8 size)
|
|
+{
|
|
+ u8 index, current_bit, bit_count;
|
|
+ u8 group_array[BITS_PER_COMMAND * GROUPS_PER_BIT + 4];
|
|
+ u8 group_index = 0;
|
|
+ u32 command = 0xFFFFFFFF;
|
|
+
|
|
+ group_array[group_index++] = 1;
|
|
+
|
|
+ for(index = 0; index < size; index++) {
|
|
+
|
|
+ current_bit = (pData[index] & 0x80) ? 1 : 0;
|
|
+ bit_count = pData[index] & 0x7f;
|
|
+
|
|
+ /*dprintk("index[%d]: bit = %d, count = %d\n", index, current_bit, bit_count);*/
|
|
+ if((current_bit == 1) && (bit_count >= 2*IR_RC5_MAX_BIT + 1)) {
|
|
+ goto process_code;
|
|
+ } else if((bit_count >= IR_RC5_MIN_BIT) && (bit_count <= IR_RC5_MAX_BIT)) {
|
|
+ group_array[group_index++] = current_bit;
|
|
+ } else if ((bit_count > IR_RC5_MAX_BIT) && (bit_count <= 2*IR_RC5_MAX_BIT)) {
|
|
+ group_array[group_index++] = current_bit;
|
|
+ group_array[group_index++] = current_bit;
|
|
+ } else {
|
|
+ goto invalid_timing;
|
|
+ }
|
|
+ if(group_index >= BITS_PER_COMMAND*GROUPS_PER_BIT)
|
|
+ goto process_code;
|
|
+
|
|
+ if((group_index == BITS_PER_COMMAND*GROUPS_PER_BIT - 1)
|
|
+ && (group_array[group_index-1] == 0)) {
|
|
+ group_array[group_index++] = 1;
|
|
+ goto process_code;
|
|
+ }
|
|
+ }
|
|
+
|
|
+process_code:
|
|
+ if(group_index == (BITS_PER_COMMAND*GROUPS_PER_BIT-1)) {
|
|
+ group_array[group_index++] = 1;
|
|
+ }
|
|
+
|
|
+ /*dprintk("group index = %d\n", group_index);*/
|
|
+ if(group_index == BITS_PER_COMMAND*GROUPS_PER_BIT) {
|
|
+ command = 0;
|
|
+ for(index = 0; index < (BITS_PER_COMMAND*GROUPS_PER_BIT); index = index + 2) {
|
|
+ /*dprintk("group[%d]:[%d]->[%d]\n", index/2, group_array[index], group_array[index+1]);*/
|
|
+ if((group_array[index] == 1) && (group_array[index+1] == 0)) {
|
|
+ command |= ( 1 << (BITS_PER_COMMAND - (index/2) - 1));
|
|
+ } else if((group_array[index] == 0) && (group_array[index+1] == 1)) {
|
|
+
|
|
+ } else {
|
|
+ command = 0xFFFFFFFF;
|
|
+ goto invalid_timing;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+invalid_timing:
|
|
+ /*dprintk("rc5 code = %x\n", command);*/
|
|
+ return command;
|
|
+}
|
|
+
|
|
+/* static void smi_ir_decode(unsigned long data) */
|
|
+static void smi_ir_decode(struct work_struct *work)
|
|
+{
|
|
+ /* struct smi_rc *ir = (struct smi_rc *)data; */
|
|
+ struct smi_rc *ir = container_of(work, struct smi_rc, work);
|
|
+ struct smi_dev *dev = ir->dev;
|
|
+ struct rc_dev *rc_dev = ir->rc_dev;
|
|
+/* int ret;
|
|
+ struct ir_raw_event ev; */
|
|
+ u32 dwIRControl, dwIRData, dwIRCode, scancode;
|
|
+ u8 index, ucIRCount, readLoop, rc5_command, rc5_system, toggle;
|
|
+
|
|
+ dwIRControl = smi_read(IR_Init_Reg);
|
|
+ /*dprintk("IR_Init_Reg = 0x%08x\n", dwIRControl);*/
|
|
+ if( dwIRControl & rbIRVld) {
|
|
+ ucIRCount = (u8) smi_read(IR_Data_Cnt);
|
|
+ /*dprintk("ir data count=%d\n", ucIRCount);*/
|
|
+ if(ucIRCount < 4) {
|
|
+ goto end_ir_decode;
|
|
+ }
|
|
+
|
|
+ readLoop = ucIRCount/4;
|
|
+ if(ucIRCount % 4)
|
|
+ readLoop += 1;
|
|
+ for(index = 0; index < readLoop; index++) {
|
|
+ dwIRData = smi_read(IR_DATA_BUFFER_BASE + (index*4));
|
|
+ /*dprintk("ir data[%d] = 0x%08x\n", index, dwIRData);*/
|
|
+
|
|
+ ir->irData[index*4 + 0] = (u8)(dwIRData);
|
|
+ ir->irData[index*4 + 1] = (u8)(dwIRData >> 8);
|
|
+ ir->irData[index*4 + 2] = (u8)(dwIRData >> 16);
|
|
+ ir->irData[index*4 + 3] = (u8)(dwIRData >> 24);
|
|
+ }
|
|
+ dwIRCode = smi_decode_rc5(ir->irData, ucIRCount);
|
|
+
|
|
+ if (dwIRCode != 0xFFFFFFFF) {
|
|
+ dprintk("rc code: %x \n", dwIRCode);
|
|
+ rc5_command = dwIRCode & 0x3F;
|
|
+ rc5_system = (dwIRCode & 0x7C0) >> 6;
|
|
+ toggle = (dwIRCode & 0x800) ? 1 : 0;
|
|
+ scancode = rc5_system << 8 | rc5_command;
|
|
+ rc_keydown(rc_dev, RC_TYPE_UNKNOWN, scancode, toggle);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ ir_raw_event_reset(rc_dev);
|
|
+ init_ir_raw_event(&ev);
|
|
+ ev.pulse = 1;
|
|
+ ev.duration = RC5_UNIT;
|
|
+ ir_raw_event_store(rc_dev, &ev);
|
|
+ for(index = 0; index < ucIRCount; index++) {
|
|
+ ev.pulse = (ir->irData[index] & 0x80) ? 1 : 0;
|
|
+ ev.duration = 20000 * (ir->irData[index] & 0x7f);
|
|
+ ir_raw_event_store(rc_dev, &ev);
|
|
+ //ir_raw_event_store_with_filter(rc_dev, &ev);
|
|
+ }
|
|
+ //ir_raw_event_set_idle(rc_dev, true);
|
|
+ ir_raw_event_handle(rc_dev); */
|
|
+ }
|
|
+end_ir_decode:
|
|
+ smi_set(IR_Init_Reg, 0x04);
|
|
+ smi_ir_enableInterrupt(ir);
|
|
+}
|
|
+static void smi_ir_irq(struct smi_rc *ir, u32 int_status)
|
|
+{
|
|
+ if(int_status & IR_X_INT) {
|
|
+ smi_ir_disableInterrupt(ir);
|
|
+ smi_ir_clearInterrupt(ir);
|
|
+ /* tasklet_schedule(&ir->tasklet);*/
|
|
+ schedule_work(&ir->work);
|
|
+ }
|
|
+}
|
|
+static int smi_ir_init(struct smi_dev *dev)
|
|
+{
|
|
+ int ret;
|
|
+ struct rc_dev *rc_dev;
|
|
+ struct smi_rc *ir = &dev->ir;
|
|
+
|
|
+ dprintk("%s\n", __func__);
|
|
+ rc_dev = rc_allocate_device();
|
|
+ if(!rc_dev)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* init input device */
|
|
+ snprintf(ir->input_name, sizeof(ir->input_name), "IR (%s)", dev->info->name);
|
|
+ snprintf(ir->input_phys, sizeof(ir->input_phys), "pci-%s/ir0", pci_name(dev->pci_dev));
|
|
+
|
|
+ rc_dev->driver_name = "SMI_PCIe";
|
|
+ rc_dev->input_phys = ir->input_phys;
|
|
+ rc_dev->input_name = ir->input_name;
|
|
+ rc_dev->input_id.bustype = BUS_PCI;
|
|
+ rc_dev->input_id.version = 1;
|
|
+ rc_dev->input_id.vendor = dev->pci_dev->subsystem_vendor;
|
|
+ rc_dev->input_id.product = dev->pci_dev->subsystem_device;
|
|
+ rc_dev->dev.parent = &dev->pci_dev->dev;
|
|
+
|
|
+ rc_dev->driver_type = RC_DRIVER_SCANCODE;
|
|
+ rc_dev->map_name = RC_MAP_DVBSKY;
|
|
+ /*
|
|
+ rc_dev->driver_type = RC_DRIVER_IR_RAW;
|
|
+ rc_set_allowed_protocols(rc_dev, RC_BIT_ALL);
|
|
+
|
|
+ rc_dev->priv = ir;
|
|
+ rc_dev->open = smi_ir_open;
|
|
+ rc_dev->close = smi_ir_close;
|
|
+ rc_dev->timeout = MS_TO_NS(10); */
|
|
+
|
|
+ ir->rc_dev = rc_dev;
|
|
+ ir->dev = dev;
|
|
+
|
|
+ /*tasklet_init(&ir->tasklet, smi_ir_decode, (unsigned long)ir);
|
|
+ tasklet_disable(&ir->tasklet); */
|
|
+ INIT_WORK(&ir->work, smi_ir_decode);
|
|
+ smi_ir_disableInterrupt(ir);
|
|
+
|
|
+ ret = rc_register_device(rc_dev);
|
|
+ if(ret)
|
|
+ goto ir_err;
|
|
+
|
|
+ /* smi_ir_start(ir);*/
|
|
+ return 0;
|
|
+ir_err:
|
|
+ rc_free_device(rc_dev);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void smi_ir_exit(struct smi_dev *dev)
|
|
+{
|
|
+ struct smi_rc *ir = &dev->ir;
|
|
+ struct rc_dev *rc_dev = ir->rc_dev;
|
|
+
|
|
+ dprintk("%s\n", __func__);
|
|
+ smi_ir_stop(ir);
|
|
+ /* tasklet_kill(&ir->tasklet);*/
|
|
+ rc_unregister_device(rc_dev);
|
|
+ ir->rc_dev = NULL;
|
|
+}
|
|
+
|
|
+/* i2c bit bus.*/
|
|
+static void smi_i2c_cfg(struct smi_dev *dev, u32 sw_ctl)
|
|
+{
|
|
+ u32 dwCtrl;
|
|
+
|
|
+ dwCtrl = smi_read(sw_ctl);
|
|
+ dwCtrl &= ~0x18; /* disable output.*/
|
|
+ dwCtrl |= 0x21; /* reset and software mode.*/
|
|
+ dwCtrl &= ~0xff00;
|
|
+ dwCtrl |= 0x6400;
|
|
+ smi_write(sw_ctl, dwCtrl);
|
|
+ msleep(1);
|
|
+ dwCtrl = smi_read(sw_ctl);
|
|
+ dwCtrl &= ~0x20;
|
|
+ smi_write(sw_ctl, dwCtrl);
|
|
+}
|
|
+static void smi_i2c_setsda(struct smi_dev *dev, int state, u32 sw_ctl)
|
|
+{
|
|
+ if(state) {
|
|
+ /* set as input.*/
|
|
+ smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN);
|
|
+ } else {
|
|
+ smi_clear(sw_ctl, SW_I2C_MSK_DAT_OUT);
|
|
+ /* set as output.*/
|
|
+ smi_set(sw_ctl, SW_I2C_MSK_DAT_EN);
|
|
+ }
|
|
+}
|
|
+static void smi_i2c_setscl(void *data, int state, u32 sw_ctl)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ if(state) {
|
|
+ /* set as input.*/
|
|
+ smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN);
|
|
+ } else {
|
|
+ smi_clear(sw_ctl, SW_I2C_MSK_CLK_OUT);
|
|
+ /* set as output.*/
|
|
+ smi_set(sw_ctl, SW_I2C_MSK_CLK_EN);
|
|
+ }
|
|
+}
|
|
+static int smi_i2c_getsda(void *data, u32 sw_ctl)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ /* set as input.*/
|
|
+ smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN);
|
|
+ udelay(1);
|
|
+ return (smi_read(sw_ctl) & SW_I2C_MSK_DAT_IN) ? 1 : 0;
|
|
+}
|
|
+static int smi_i2c_getscl(void *data, u32 sw_ctl)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ /* set as input.*/
|
|
+ smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN);
|
|
+ udelay(1);
|
|
+ return (smi_read(sw_ctl) & SW_I2C_MSK_CLK_IN) ? 1 : 0;
|
|
+}
|
|
+/* i2c 0.*/
|
|
+static void smi_i2c0_setsda(void *data, int state)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ smi_i2c_setsda(dev, state, I2C_A_SW_CTL);
|
|
+}
|
|
+static void smi_i2c0_setscl(void *data, int state)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ smi_i2c_setscl(dev, state, I2C_A_SW_CTL);
|
|
+}
|
|
+static int smi_i2c0_getsda(void *data)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ return smi_i2c_getsda(dev, I2C_A_SW_CTL);
|
|
+}
|
|
+static int smi_i2c0_getscl(void *data)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ return smi_i2c_getscl(dev, I2C_A_SW_CTL);
|
|
+}
|
|
+/* i2c 1.*/
|
|
+static void smi_i2c1_setsda(void *data, int state)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ smi_i2c_setsda(dev, state, I2C_B_SW_CTL);
|
|
+}
|
|
+static void smi_i2c1_setscl(void *data, int state)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ smi_i2c_setscl(dev, state, I2C_B_SW_CTL);
|
|
+}
|
|
+static int smi_i2c1_getsda(void *data)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ return smi_i2c_getsda(dev, I2C_B_SW_CTL);
|
|
+}
|
|
+static int smi_i2c1_getscl(void *data)
|
|
+{
|
|
+ struct smi_dev *dev = data;
|
|
+ return smi_i2c_getscl(dev, I2C_B_SW_CTL);
|
|
+}
|
|
+
|
|
+static int smi_i2c_init(struct smi_dev *dev)
|
|
+{
|
|
+ int ret;
|
|
+ dprintk("%s\n", __func__);
|
|
+
|
|
+ /* i2c bus 0 */
|
|
+ smi_i2c_cfg(dev, I2C_A_SW_CTL);
|
|
+ i2c_set_adapdata(&dev->i2c_bus[0], dev);
|
|
+ strcpy(dev->i2c_bus[0].name, "SMI-I2C0");
|
|
+ dev->i2c_bus[0].owner = THIS_MODULE;
|
|
+ dev->i2c_bus[0].dev.parent = &dev->pci_dev->dev;
|
|
+ dev->i2c_bus[0].algo_data = &dev->i2c_bit[0];
|
|
+ dev->i2c_bit[0].data = dev;
|
|
+ dev->i2c_bit[0].setsda = smi_i2c0_setsda;
|
|
+ dev->i2c_bit[0].setscl = smi_i2c0_setscl;
|
|
+ dev->i2c_bit[0].getsda = smi_i2c0_getsda;
|
|
+ dev->i2c_bit[0].getscl = smi_i2c0_getscl;
|
|
+ dev->i2c_bit[0].udelay = 12;
|
|
+ dev->i2c_bit[0].timeout = 10;
|
|
+ /* Raise SCL and SDA */
|
|
+ smi_i2c0_setsda(dev, 1);
|
|
+ smi_i2c0_setscl(dev, 1);
|
|
+
|
|
+ ret = i2c_bit_add_bus(&dev->i2c_bus[0]);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ /* i2c bus 1 */
|
|
+ smi_i2c_cfg(dev, I2C_B_SW_CTL);
|
|
+ i2c_set_adapdata(&dev->i2c_bus[1], dev);
|
|
+ strcpy(dev->i2c_bus[1].name, "SMI-I2C1");
|
|
+ dev->i2c_bus[1].owner = THIS_MODULE;
|
|
+ dev->i2c_bus[1].dev.parent = &dev->pci_dev->dev;
|
|
+ dev->i2c_bus[1].algo_data = &dev->i2c_bit[1];
|
|
+ dev->i2c_bit[1].data = dev;
|
|
+ dev->i2c_bit[1].setsda = smi_i2c1_setsda;
|
|
+ dev->i2c_bit[1].setscl = smi_i2c1_setscl;
|
|
+ dev->i2c_bit[1].getsda = smi_i2c1_getsda;
|
|
+ dev->i2c_bit[1].getscl = smi_i2c1_getscl;
|
|
+ dev->i2c_bit[1].udelay = 12;
|
|
+ dev->i2c_bit[1].timeout = 10;
|
|
+ /* Raise SCL and SDA */
|
|
+ smi_i2c1_setsda(dev, 1);
|
|
+ smi_i2c1_setscl(dev, 1);
|
|
+
|
|
+ ret = i2c_bit_add_bus(&dev->i2c_bus[1]);
|
|
+ if (ret < 0)
|
|
+ i2c_del_adapter(&dev->i2c_bus[0]);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+static void smi_i2c_exit(struct smi_dev *dev)
|
|
+{
|
|
+ dprintk("%s\n", __func__);
|
|
+ i2c_del_adapter(&dev->i2c_bus[0]);
|
|
+ i2c_del_adapter(&dev->i2c_bus[1]);
|
|
+}
|
|
+
|
|
+static int smi_read_eeprom(struct i2c_adapter *i2c, u16 reg, u8 *data, u16 size)
|
|
+{
|
|
+ int ret;
|
|
+ u8 b0[2] = { (reg >> 8) & 0xff, reg & 0xff };
|
|
+
|
|
+ struct i2c_msg msg[] = {
|
|
+ { .addr = 0x50, .flags = 0,
|
|
+ .buf = b0, .len = 2 },
|
|
+ { .addr = 0x50, .flags = I2C_M_RD,
|
|
+ .buf = data, .len = size }
|
|
+ };
|
|
+
|
|
+ ret = i2c_transfer(i2c, msg, 2);
|
|
+
|
|
+ if (ret != 2) {
|
|
+ printk(KERN_ERR "%s: reg=0x%x (error=%d)\n",
|
|
+ __func__, reg, ret);
|
|
+ return ret;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void smi_port_disableInterrupt(struct smi_port *port)
|
|
+{
|
|
+ struct smi_dev *dev = port->dev;
|
|
+ smi_write(MSI_INT_ENA_CLR, (port->_dmaInterruptCH0 | port->_dmaInterruptCH1));
|
|
+}
|
|
+static void smi_port_enableInterrupt(struct smi_port *port)
|
|
+{
|
|
+ struct smi_dev *dev = port->dev;
|
|
+ smi_write(MSI_INT_ENA_SET, (port->_dmaInterruptCH0 | port->_dmaInterruptCH1));
|
|
+}
|
|
+static void smi_port_clearInterrupt(struct smi_port *port)
|
|
+{
|
|
+ struct smi_dev *dev = port->dev;
|
|
+ smi_write(MSI_INT_STATUS_CLR, (port->_dmaInterruptCH0 | port->_dmaInterruptCH1));
|
|
+}
|
|
+/* tasklet handler: DMA data to dmx.*/
|
|
+static void smi_dma_xfer(unsigned long data)
|
|
+{
|
|
+ struct smi_port *port = (struct smi_port *) data;
|
|
+ struct smi_dev *dev = port->dev;
|
|
+ u32 intr_status, finishedData, dmaManagement;
|
|
+ u8 dmaChan0State, dmaChan1State;
|
|
+
|
|
+ intr_status = port->_int_status;
|
|
+ dmaManagement = smi_read(port->DMA_MANAGEMENT);
|
|
+ dmaChan0State = (u8)((dmaManagement & 0x00000030) >> 4);
|
|
+ dmaChan1State = (u8)((dmaManagement & 0x00300000) >> 20);
|
|
+
|
|
+ if(dmaChan0State == 0x10) {
|
|
+ dprintk("DMA CH0 engine cancel!\n");
|
|
+ } else if(dmaChan0State == 0x11) {
|
|
+ dprintk("DMA CH0 engine timeout!\n");
|
|
+ }
|
|
+
|
|
+ if(dmaChan1State == 0x10) {
|
|
+ dprintk("DMA CH1 engine cancel!\n");
|
|
+ } else if(dmaChan1State == 0x11) {
|
|
+ dprintk("DMA CH1 engine timeout!\n");
|
|
+ }
|
|
+
|
|
+ /* CH-0 DMA interrupt.*/
|
|
+ if((intr_status & port->_dmaInterruptCH0) && (dmaChan0State == 0x01)) {
|
|
+ dprintk("Port[%d]-DMA CH0 engine complete successful !\n", port->idx);
|
|
+ finishedData = smi_read(port->DMA_CHAN0_TRANS_STATE);
|
|
+ finishedData &= 0x003FFFFF;
|
|
+ /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0] indicate dma total transfer length
|
|
+ and zero of [21:0] indicate dma total transfer length equal to 0x400000 (4MB)*/
|
|
+ if(finishedData == 0)
|
|
+ finishedData = 0x00400000;
|
|
+ if(finishedData != SMI_TS_DMA_BUF_SIZE) {
|
|
+ dprintk("DMA CH0 engine complete length mismatched, finish data=%d !\n", finishedData);
|
|
+ }
|
|
+ dprintk("DMA CH0 data[0]=%x,data[1]=%x.\n", port->cpu_addr[0][0],port->cpu_addr[0][1]);
|
|
+ dvb_dmx_swfilter_packets(&port->demux, port->cpu_addr[0], (finishedData / 188));
|
|
+ /*dvb_dmx_swfilter(&port->demux, port->cpu_addr[0], finishedData);*/
|
|
+ }
|
|
+ /* CH-1 DMA interrupt.*/
|
|
+ if((intr_status & port->_dmaInterruptCH1) && (dmaChan1State == 0x01)) {
|
|
+ dprintk("Port[%d]-DMA CH1 engine complete successful !\n", port->idx);
|
|
+ finishedData = smi_read(port->DMA_CHAN1_TRANS_STATE);
|
|
+ finishedData &= 0x003FFFFF;
|
|
+ /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0] indicate dma total transfer length
|
|
+ and zero of [21:0] indicate dma total transfer length equal to 0x400000 (4MB)*/
|
|
+ if(finishedData == 0)
|
|
+ finishedData = 0x00400000;
|
|
+ if(finishedData != SMI_TS_DMA_BUF_SIZE) {
|
|
+ dprintk("DMA CH1 engine complete length mismatched, finish data=%d !\n", finishedData);
|
|
+ }
|
|
+ dvb_dmx_swfilter_packets(&port->demux, port->cpu_addr[1], (finishedData / 188));
|
|
+ /*dvb_dmx_swfilter(&port->demux, port->cpu_addr[1], finishedData);*/
|
|
+ }
|
|
+ /* restart DMA.*/
|
|
+ if(intr_status & port->_dmaInterruptCH0)
|
|
+ dmaManagement |= 0x00000002;
|
|
+ if(intr_status & port->_dmaInterruptCH1)
|
|
+ dmaManagement |= 0x00020000;
|
|
+ smi_write(port->DMA_MANAGEMENT, dmaManagement);
|
|
+ /* Re-enable interrupts */
|
|
+ smi_port_enableInterrupt(port);
|
|
+}
|
|
+
|
|
+static void smi_port_dma_free(struct smi_port *port)
|
|
+{
|
|
+ if(port->cpu_addr[0]) {
|
|
+ pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE,
|
|
+ port->cpu_addr[0], port->dma_addr[0]);
|
|
+ port->cpu_addr[0] = NULL;
|
|
+ }
|
|
+ if(port->cpu_addr[1]) {
|
|
+ pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE,
|
|
+ port->cpu_addr[1], port->dma_addr[1]);
|
|
+ port->cpu_addr[1] = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int smi_port_init(struct smi_port *port, int dmaChanUsed)
|
|
+{
|
|
+ dprintk("%s, port %d, dmaused %d\n", __func__, port->idx, dmaChanUsed);
|
|
+ port->enable = 0;
|
|
+ if(port->idx == 0) {
|
|
+ /* Port A */
|
|
+ port->_dmaInterruptCH0 = dmaChanUsed & 0x01;
|
|
+ port->_dmaInterruptCH1 = dmaChanUsed & 0x02;
|
|
+
|
|
+ port->DMA_CHAN0_ADDR_LOW = DMA_PORTA_CHAN0_ADDR_LOW;
|
|
+ port->DMA_CHAN0_ADDR_HI = DMA_PORTA_CHAN0_ADDR_HI;
|
|
+ port->DMA_CHAN0_TRANS_STATE = DMA_PORTA_CHAN0_TRANS_STATE;
|
|
+ port->DMA_CHAN0_CONTROL = DMA_PORTA_CHAN0_CONTROL;
|
|
+ port->DMA_CHAN1_ADDR_LOW = DMA_PORTA_CHAN1_ADDR_LOW;
|
|
+ port->DMA_CHAN1_ADDR_HI = DMA_PORTA_CHAN1_ADDR_HI;
|
|
+ port->DMA_CHAN1_TRANS_STATE = DMA_PORTA_CHAN1_TRANS_STATE;
|
|
+ port->DMA_CHAN1_CONTROL = DMA_PORTA_CHAN1_CONTROL;
|
|
+ port->DMA_MANAGEMENT = DMA_PORTA_MANAGEMENT;
|
|
+ } else {
|
|
+ /* Port B */
|
|
+ port->_dmaInterruptCH0 = (dmaChanUsed << 2) & 0x04;
|
|
+ port->_dmaInterruptCH1 = (dmaChanUsed << 2) & 0x08;
|
|
+
|
|
+ port->DMA_CHAN0_ADDR_LOW = DMA_PORTB_CHAN0_ADDR_LOW;
|
|
+ port->DMA_CHAN0_ADDR_HI = DMA_PORTB_CHAN0_ADDR_HI;
|
|
+ port->DMA_CHAN0_TRANS_STATE = DMA_PORTB_CHAN0_TRANS_STATE;
|
|
+ port->DMA_CHAN0_CONTROL = DMA_PORTB_CHAN0_CONTROL;
|
|
+ port->DMA_CHAN1_ADDR_LOW = DMA_PORTB_CHAN1_ADDR_LOW;
|
|
+ port->DMA_CHAN1_ADDR_HI = DMA_PORTB_CHAN1_ADDR_HI;
|
|
+ port->DMA_CHAN1_TRANS_STATE = DMA_PORTB_CHAN1_TRANS_STATE;
|
|
+ port->DMA_CHAN1_CONTROL = DMA_PORTB_CHAN1_CONTROL;
|
|
+ port->DMA_MANAGEMENT = DMA_PORTB_MANAGEMENT;
|
|
+ }
|
|
+
|
|
+ if(port->_dmaInterruptCH0) {
|
|
+ port->cpu_addr[0] = pci_alloc_consistent(port->dev->pci_dev,
|
|
+ SMI_TS_DMA_BUF_SIZE,
|
|
+ &port->dma_addr[0]);
|
|
+ if(!port->cpu_addr[0]) {
|
|
+ dprintk("%s(): Port[%d] DMA CH0 memory allocation failed!\n", __func__, port->idx);
|
|
+ goto err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(port->_dmaInterruptCH1) {
|
|
+ port->cpu_addr[1] = pci_alloc_consistent(port->dev->pci_dev,
|
|
+ SMI_TS_DMA_BUF_SIZE,
|
|
+ &port->dma_addr[1]);
|
|
+ if(!port->cpu_addr[1]) {
|
|
+ dprintk("%s(): Port[%d] DMA CH1 memory allocation failed!\n", __func__, port->idx);
|
|
+ goto err;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ smi_port_disableInterrupt(port);
|
|
+ tasklet_init(&port->tasklet, smi_dma_xfer, (unsigned long)port);
|
|
+ tasklet_disable(&port->tasklet);
|
|
+ port->enable = 1;
|
|
+ return 0;
|
|
+err:
|
|
+ smi_port_dma_free(port);
|
|
+ return -ENOMEM;
|
|
+}
|
|
+
|
|
+static void smi_port_exit(struct smi_port *port)
|
|
+{
|
|
+ dprintk("%s\n", __func__);
|
|
+ smi_port_disableInterrupt(port);
|
|
+ tasklet_kill(&port->tasklet);
|
|
+ smi_port_dma_free(port);
|
|
+ port->enable = 0;
|
|
+}
|
|
+
|
|
+static void smi_port_irq(struct smi_port *port, u32 int_status)
|
|
+{
|
|
+ u32 port_req_irq = port->_dmaInterruptCH0 | port->_dmaInterruptCH1;
|
|
+ if(int_status & port_req_irq) {
|
|
+ smi_port_disableInterrupt(port);
|
|
+ port->_int_status = int_status;
|
|
+ smi_port_clearInterrupt(port);
|
|
+ tasklet_schedule(&port->tasklet);
|
|
+ }
|
|
+}
|
|
+static irqreturn_t smi_irq_handler(int irq, void *dev_id)
|
|
+{
|
|
+ struct smi_dev *dev = dev_id;
|
|
+ struct smi_port *port0 = &dev->ts_port[0];
|
|
+ struct smi_port *port1 = &dev->ts_port[1];
|
|
+ struct smi_rc *ir = &dev->ir;
|
|
+
|
|
+ u32 intr_status = smi_read(MSI_INT_STATUS);
|
|
+
|
|
+ /* ts0 interrupt.*/
|
|
+ if(dev->info->ts_0)
|
|
+ smi_port_irq(port0, intr_status);
|
|
+
|
|
+ /* ts1 interrupt.*/
|
|
+ if(dev->info->ts_1)
|
|
+ smi_port_irq(port1, intr_status);
|
|
+
|
|
+ /* ir interrupt.*/
|
|
+ smi_ir_irq(ir, intr_status);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static struct dvbsky_m88rs6000_config smi_dvbsky_s952_cfg = {
|
|
+ .demod_address = 0x69, /* 0xd2 >> 1 */
|
|
+ .pin_ctrl = 0x82,
|
|
+ .ci_mode = 0,
|
|
+ .ts_mode = 0,
|
|
+ //.tuner_readstops = 1,
|
|
+};
|
|
+
|
|
+static struct dvbsky_m88ds3103_config smi_dvbsky_s950_cfg = {
|
|
+ .demod_address = 0x68,
|
|
+ .pin_ctrl = 0x82,
|
|
+ .ci_mode = 0,
|
|
+ .ts_mode = 0,
|
|
+ //.tuner_readstops = 1,
|
|
+};
|
|
+
|
|
+static int smi_fe_init(struct smi_port *port)
|
|
+{
|
|
+ int ret = 0;
|
|
+ struct smi_dev *dev = port->dev;
|
|
+ struct dvb_adapter *adap = &port->dvb_adapter;
|
|
+ struct i2c_adapter *i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1];
|
|
+ u8 mac_ee[16];
|
|
+
|
|
+ dprintk("%s: port %d, fe_type = %d\n", __func__, port->idx, port->fe_type);
|
|
+ switch(port->fe_type) {
|
|
+ case DVBSKY_FE_M88RS6000:
|
|
+ port->fe = dvb_attach(dvbsky_m88rs6000_attach, &smi_dvbsky_s952_cfg, i2c);
|
|
+ break;
|
|
+ case DVBSKY_FE_M88DS3103:
|
|
+ port->fe = dvb_attach(dvbsky_m88ds3103_attach, &smi_dvbsky_s950_cfg, i2c);
|
|
+ break;
|
|
+ case DVBSKY_FE_SIT2:
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ if(!port->fe)
|
|
+ return -ENODEV;
|
|
+
|
|
+ ret = dvb_register_frontend(adap, port->fe);
|
|
+ if(ret < 0) {
|
|
+ dvb_frontend_detach(port->fe);
|
|
+ return ret;
|
|
+ }
|
|
+ /*init MAC.*/
|
|
+ ret = smi_read_eeprom(&dev->i2c_bus[0], 0xc0, mac_ee, 16);
|
|
+ printk(KERN_INFO "DVBSky PCIe MAC= %pM\n", mac_ee + (port->idx)*8);
|
|
+ memcpy(adap->proposed_mac, mac_ee + (port->idx)*8, 6);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+static void smi_fe_exit(struct smi_port *port)
|
|
+{
|
|
+ dprintk("%s\n", __func__);
|
|
+ dvb_unregister_frontend(port->fe);
|
|
+ dvb_frontend_detach(port->fe);
|
|
+}
|
|
+static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
|
|
+ int (*start_feed)(struct dvb_demux_feed *),
|
|
+ int (*stop_feed)(struct dvb_demux_feed *),
|
|
+ void *priv)
|
|
+{
|
|
+ dvbdemux->priv = priv;
|
|
+
|
|
+ dvbdemux->filternum = 256;
|
|
+ dvbdemux->feednum = 256;
|
|
+ dvbdemux->start_feed = start_feed;
|
|
+ dvbdemux->stop_feed = stop_feed;
|
|
+ dvbdemux->write_to_decoder = NULL;
|
|
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
|
|
+ DMX_SECTION_FILTERING |
|
|
+ DMX_MEMORY_BASED_FILTERING);
|
|
+ return dvb_dmx_init(dvbdemux);
|
|
+}
|
|
+
|
|
+static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
|
|
+ struct dvb_demux *dvbdemux,
|
|
+ struct dmx_frontend *hw_frontend,
|
|
+ struct dmx_frontend *mem_frontend,
|
|
+ struct dvb_adapter *dvb_adapter)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ dmxdev->filternum = 256;
|
|
+ dmxdev->demux = &dvbdemux->dmx;
|
|
+ dmxdev->capabilities = 0;
|
|
+ ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ hw_frontend->source = DMX_FRONTEND_0;
|
|
+ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
|
|
+ mem_frontend->source = DMX_MEMORY_FE;
|
|
+ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
|
|
+ return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
|
|
+}
|
|
+
|
|
+static u32 smi_config_DMA(struct smi_port *port)
|
|
+{
|
|
+ struct smi_dev *dev = port->dev;
|
|
+ u32 totalLength = 0, dmaMemPtrLow, dmaMemPtrHi, dmaCtlReg, dmaManagement = 0;
|
|
+ u8 chanLatencyTimer = 0, dmaChanEnable = 1, dmaTransStart = 1;
|
|
+ u32 tlpTransUnit = DMA_TRANS_UNIT_188;
|
|
+ u8 tlpTc = 0, tlpTd = 1, tlpEp = 0, tlpAttr = 0;
|
|
+ u64 mem;
|
|
+
|
|
+ dmaManagement = smi_read(port->DMA_MANAGEMENT);
|
|
+ /* Setup Channel-0 */
|
|
+ if (port->_dmaInterruptCH0) {
|
|
+ totalLength = SMI_TS_DMA_BUF_SIZE;
|
|
+ mem = port->dma_addr[0];
|
|
+ dmaMemPtrLow = mem & 0xffffffff;
|
|
+ dmaMemPtrHi = mem >> 32;
|
|
+ dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25) |
|
|
+ (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30);
|
|
+ dmaManagement |= dmaChanEnable | (dmaTransStart << 1) | (chanLatencyTimer << 8);
|
|
+ /* write DMA register, start DMA engine */
|
|
+ smi_write(port->DMA_CHAN0_ADDR_LOW, dmaMemPtrLow);
|
|
+ smi_write(port->DMA_CHAN0_ADDR_HI, dmaMemPtrHi);
|
|
+ smi_write(port->DMA_CHAN0_CONTROL, dmaCtlReg);
|
|
+ }
|
|
+ /* Setup Channel-1 */
|
|
+ if (port->_dmaInterruptCH1) {
|
|
+ totalLength = SMI_TS_DMA_BUF_SIZE;
|
|
+ mem = port->dma_addr[1];
|
|
+ dmaMemPtrLow = mem & 0xffffffff;
|
|
+ dmaMemPtrHi = mem >> 32;
|
|
+ dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25) |
|
|
+ (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30);
|
|
+ dmaManagement |= (dmaChanEnable << 16) | (dmaTransStart << 17) | (chanLatencyTimer << 24);
|
|
+ /* write DMA register, start DMA engine */
|
|
+ smi_write(port->DMA_CHAN1_ADDR_LOW, dmaMemPtrLow);
|
|
+ smi_write(port->DMA_CHAN1_ADDR_HI, dmaMemPtrHi);
|
|
+ smi_write(port->DMA_CHAN1_CONTROL, dmaCtlReg);
|
|
+ }
|
|
+ return dmaManagement;
|
|
+}
|
|
+
|
|
+static int smi_start_feed(struct dvb_demux_feed *dvbdmxfeed)
|
|
+{
|
|
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
|
|
+ struct smi_port *port = dvbdmx->priv;
|
|
+ struct smi_dev *dev = port->dev;
|
|
+ u32 dmaManagement;
|
|
+
|
|
+ if (port->users++ == 0) {
|
|
+ dmaManagement = smi_config_DMA(port);
|
|
+ smi_port_clearInterrupt(port);
|
|
+ smi_port_enableInterrupt(port);
|
|
+ smi_write(port->DMA_MANAGEMENT, dmaManagement);
|
|
+ tasklet_enable(&port->tasklet);
|
|
+ }
|
|
+ return port->users;
|
|
+}
|
|
+
|
|
+static int smi_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
|
|
+{
|
|
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
|
|
+ struct smi_port *port = dvbdmx->priv;
|
|
+ struct smi_dev *dev = port->dev;
|
|
+
|
|
+ if (--port->users)
|
|
+ return port->users;
|
|
+
|
|
+ tasklet_disable(&port->tasklet);
|
|
+ smi_port_disableInterrupt(port);
|
|
+ smi_clear(port->DMA_MANAGEMENT, 0x30003);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int smi_dvb_init(struct smi_port *port)
|
|
+{
|
|
+ int ret;
|
|
+ struct dvb_adapter *adap = &port->dvb_adapter;
|
|
+ struct dvb_demux *dvbdemux = &port->demux;
|
|
+
|
|
+ dprintk("%s, port %d\n", __func__, port->idx);
|
|
+
|
|
+ ret = dvb_register_adapter(adap, "SMI_DVB", THIS_MODULE,
|
|
+ &port->dev->pci_dev->dev,
|
|
+ adapter_nr);
|
|
+ if (ret < 0) {
|
|
+ printk(KERN_ERR "smi_dvb: Could not register adapter."
|
|
+ "Check if you enabled enough adapters in dvb-core!\n");
|
|
+ return ret;
|
|
+ }
|
|
+ ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
|
|
+ smi_start_feed,
|
|
+ smi_stop_feed, port);
|
|
+ if (ret < 0)
|
|
+ goto err_del_dvb_register_adapter;
|
|
+
|
|
+ ret = my_dvb_dmxdev_ts_card_init(&port->dmxdev, &port->demux,
|
|
+ &port->hw_frontend,
|
|
+ &port->mem_frontend, adap);
|
|
+ if (ret < 0)
|
|
+ goto err_del_dvb_dmx;
|
|
+
|
|
+ ret = dvb_net_init(adap, &port->dvbnet, port->dmxdev.demux);
|
|
+ if (ret < 0)
|
|
+ goto err_del_dvb_dmxdev;
|
|
+ return 0;
|
|
+err_del_dvb_dmxdev:
|
|
+ dvbdemux->dmx.close(&dvbdemux->dmx);
|
|
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend);
|
|
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend);
|
|
+ dvb_dmxdev_release(&port->dmxdev);
|
|
+err_del_dvb_dmx:
|
|
+ dvb_dmx_release(&port->demux);
|
|
+err_del_dvb_register_adapter:
|
|
+ dvb_unregister_adapter(&port->dvb_adapter);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void smi_dvb_exit(struct smi_port *port)
|
|
+{
|
|
+ struct dvb_demux *dvbdemux = &port->demux;
|
|
+
|
|
+ dprintk("%s\n", __func__);
|
|
+
|
|
+ dvb_net_release(&port->dvbnet);
|
|
+
|
|
+ dvbdemux->dmx.close(&dvbdemux->dmx);
|
|
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend);
|
|
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend);
|
|
+ dvb_dmxdev_release(&port->dmxdev);
|
|
+ dvb_dmx_release(&port->demux);
|
|
+
|
|
+ dvb_unregister_adapter(&port->dvb_adapter);
|
|
+}
|
|
+static int smi_port_attach(struct smi_dev *dev, struct smi_port *port, int index)
|
|
+{
|
|
+ int ret, dmachs;
|
|
+ dprintk("%s port %d\n", __func__, index);
|
|
+ port->dev = dev;
|
|
+ port->idx = index;
|
|
+
|
|
+ port->fe_type = (index == 0) ? dev->info->fe_0 : dev->info->fe_1;
|
|
+ dmachs = (index == 0) ? dev->info->ts_0 : dev->info->ts_1;
|
|
+
|
|
+ /* port init.*/
|
|
+ ret = smi_port_init(port, dmachs);
|
|
+ if(ret < 0)
|
|
+ return ret;
|
|
+ /* dvb init.*/
|
|
+ ret = smi_dvb_init(port);
|
|
+ if(ret < 0)
|
|
+ goto err_del_port_init;
|
|
+ /* fe init.*/
|
|
+ ret = smi_fe_init(port);
|
|
+ if(ret < 0)
|
|
+ goto err_del_dvb_init;
|
|
+ return 0;
|
|
+err_del_dvb_init:
|
|
+ smi_dvb_exit(port);
|
|
+err_del_port_init:
|
|
+ smi_port_exit(port);
|
|
+ return ret;
|
|
+}
|
|
+static void smi_port_detach(struct smi_port *port)
|
|
+{
|
|
+ dprintk("%s\n", __func__);
|
|
+ smi_fe_exit(port);
|
|
+ smi_dvb_exit(port);
|
|
+ smi_port_exit(port);
|
|
+}
|
|
+
|
|
+static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
+{
|
|
+ struct smi_dev *dev;
|
|
+ int ret = -ENOMEM;
|
|
+
|
|
+ if (pci_enable_device(pdev) < 0)
|
|
+ return -ENODEV;
|
|
+
|
|
+ dev = kzalloc(sizeof(struct smi_dev), GFP_KERNEL);
|
|
+ if (!dev) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err_pci_disable_device;
|
|
+ }
|
|
+
|
|
+ dev->pci_dev = pdev;
|
|
+ pci_set_drvdata(pdev, dev);
|
|
+ dev->info = (struct smi_cfg_info *) id->driver_data;
|
|
+ printk(KERN_INFO "SMI PCIe driver detected: %s\n", dev->info->name);
|
|
+
|
|
+ dev->nr = dev->info->type;
|
|
+ dev->lmmio = ioremap(pci_resource_start(dev->pci_dev, 0),
|
|
+ pci_resource_len(dev->pci_dev, 0));
|
|
+ if (!dev->lmmio) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err_kfree;
|
|
+ }
|
|
+
|
|
+ /* should we set to 32bit DMA???*/
|
|
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
|
|
+ if (ret < 0)
|
|
+ goto err_pci_iounmap;
|
|
+
|
|
+ pci_set_master(pdev);
|
|
+
|
|
+ ret = smi_hw_init(dev);
|
|
+ if (ret < 0)
|
|
+ goto err_pci_iounmap;
|
|
+
|
|
+ ret = smi_i2c_init(dev);
|
|
+ if (ret < 0)
|
|
+ goto err_pci_iounmap;
|
|
+
|
|
+ if(dev->info->ts_0) {
|
|
+ ret = smi_port_attach(dev, &dev->ts_port[0], 0);
|
|
+ if(ret < 0)
|
|
+ goto err_del_i2c_adaptor;
|
|
+ }
|
|
+
|
|
+ if(dev->info->ts_1) {
|
|
+ ret = smi_port_attach(dev, &dev->ts_port[1], 1);
|
|
+ if(ret < 0)
|
|
+ goto err_del_port0_attach;
|
|
+ }
|
|
+
|
|
+ /* to do: ir init ???.*/
|
|
+ ret = smi_ir_init(dev);
|
|
+ if(ret < 0)
|
|
+ goto err_del_port1_attach;
|
|
+
|
|
+#ifdef CONFIG_PCI_MSI /* to do msi interrupt.???*/
|
|
+ if (pci_msi_enabled())
|
|
+ ret = pci_enable_msi(dev->pci_dev);
|
|
+ if (ret) {
|
|
+ dprintk("MSI not available.\n");
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ ret = request_irq(dev->pci_dev->irq, smi_irq_handler,
|
|
+ IRQF_SHARED, "SMI_PCIE", dev);
|
|
+ if (ret < 0)
|
|
+ goto err_del_ir;
|
|
+
|
|
+ smi_ir_start(&dev->ir);
|
|
+ return 0;
|
|
+err_del_ir:
|
|
+ smi_ir_exit(dev);
|
|
+err_del_port1_attach:
|
|
+ if(dev->info->ts_1)
|
|
+ smi_port_detach(&dev->ts_port[1]);
|
|
+err_del_port0_attach:
|
|
+ if(dev->info->ts_0)
|
|
+ smi_port_detach(&dev->ts_port[0]);
|
|
+err_del_i2c_adaptor:
|
|
+ smi_i2c_exit(dev);
|
|
+err_pci_iounmap:
|
|
+ iounmap(dev->lmmio);
|
|
+err_kfree:
|
|
+ pci_set_drvdata(pdev, 0);
|
|
+ kfree(dev);
|
|
+err_pci_disable_device:
|
|
+ pci_disable_device(pdev);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void smi_remove(struct pci_dev *pdev)
|
|
+{
|
|
+ struct smi_dev *dev = pci_get_drvdata(pdev);
|
|
+ smi_write(MSI_INT_ENA_CLR, ALL_INT);
|
|
+ free_irq(dev->pci_dev->irq, dev);
|
|
+#ifdef CONFIG_PCI_MSI
|
|
+ pci_disable_msi(dev->pci_dev);
|
|
+#endif
|
|
+ if(dev->info->ts_1)
|
|
+ smi_port_detach(&dev->ts_port[1]);
|
|
+ if(dev->info->ts_0)
|
|
+ smi_port_detach(&dev->ts_port[0]);
|
|
+
|
|
+ smi_i2c_exit(dev);
|
|
+ smi_ir_exit(dev);
|
|
+ iounmap(dev->lmmio);
|
|
+ pci_set_drvdata(pdev, 0);
|
|
+ pci_disable_device(pdev);
|
|
+ kfree(dev);
|
|
+}
|
|
+
|
|
+static struct smi_cfg_info dvbsky_s952_cfg = {
|
|
+ .type = SMI_DVBSKY_S952,
|
|
+ .name = "DVBSky S952 V3",
|
|
+ .ts_0 = 0x03,
|
|
+ .ts_1 = 0x03,
|
|
+ .fe_0 = DVBSKY_FE_M88RS6000,
|
|
+ .fe_1 = DVBSKY_FE_M88RS6000,
|
|
+};
|
|
+
|
|
+static struct smi_cfg_info dvbsky_s950_cfg = {
|
|
+ .type = SMI_DVBSKY_S950,
|
|
+ .name = "DVBSky S950 V3",
|
|
+ .ts_0 = 0x00,
|
|
+ .ts_1 = 0x03,
|
|
+ .fe_0 = DVBSKY_FE_NULL,
|
|
+ .fe_1 = DVBSKY_FE_M88DS3103,
|
|
+};
|
|
+
|
|
+/* PCI IDs */
|
|
+#define SMI_ID(_subvend, _subdev, _driverdata) { \
|
|
+ .vendor = SMI_VID, .device = SMI_PID, \
|
|
+ .subvendor = _subvend, .subdevice = _subdev, \
|
|
+ .driver_data = (unsigned long)&_driverdata }
|
|
+
|
|
+static const struct pci_device_id smi_id_table[] = {
|
|
+ SMI_ID(0x4254, 0x0552, dvbsky_s952_cfg),
|
|
+ SMI_ID(0x4254, 0x0550, dvbsky_s950_cfg),
|
|
+ {0}
|
|
+};
|
|
+MODULE_DEVICE_TABLE(pci, smi_id_table);
|
|
+
|
|
+static struct pci_driver smipcie_driver = {
|
|
+ .name = "DVBSky SMI PCIe driver",
|
|
+ .id_table = smi_id_table,
|
|
+ .probe = smi_probe,
|
|
+ .remove = smi_remove,
|
|
+};
|
|
+
|
|
+module_pci_driver(smipcie_driver);
|
|
+
|
|
+MODULE_DESCRIPTION("SMI PCIe driver");
|
|
+MODULE_AUTHOR("Max nibble");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h
|
|
new file mode 100644
|
|
index 0000000..7f25859
|
|
--- /dev/null
|
|
+++ b/drivers/media/pci/smipcie/smipcie.h
|
|
@@ -0,0 +1,304 @@
|
|
+/*
|
|
+ SMI PCIe driver for DVBSky cards.
|
|
+ Copyright (C) 2014 Max Nibble <nibble.max@gmail.com>
|
|
+
|
|
+ */
|
|
+#ifndef _SMI_PCIE_H_
|
|
+#define _SMI_PCIE_H_
|
|
+
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/i2c-algo-bit.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/slab.h>
|
|
+#include <media/rc-core.h>
|
|
+
|
|
+#include "demux.h"
|
|
+#include "dmxdev.h"
|
|
+#include "dvb_demux.h"
|
|
+#include "dvb_frontend.h"
|
|
+#include "dvb_net.h"
|
|
+#include "dvbdev.h"
|
|
+
|
|
+/* -------- Register Base -------- */
|
|
+#define MSI_CONTROL_REG_BASE 0x0800
|
|
+#define SYSTEM_CONTROL_REG_BASE 0x0880
|
|
+#define PCIE_EP_DEBUG_REG_BASE 0x08C0
|
|
+#define IR_CONTROL_REG_BASE 0x0900
|
|
+#define I2C_A_CONTROL_REG_BASE 0x0940
|
|
+#define I2C_B_CONTROL_REG_BASE 0x0980
|
|
+#define ATV_PORTA_CONTROL_REG_BASE 0x09C0
|
|
+#define DTV_PORTA_CONTROL_REG_BASE 0x0A00
|
|
+#define AES_PORTA_CONTROL_REG_BASE 0x0A80
|
|
+#define DMA_PORTA_CONTROL_REG_BASE 0x0AC0
|
|
+#define ATV_PORTB_CONTROL_REG_BASE 0x0B00
|
|
+#define DTV_PORTB_CONTROL_REG_BASE 0x0B40
|
|
+#define AES_PORTB_CONTROL_REG_BASE 0x0BC0
|
|
+#define DMA_PORTB_CONTROL_REG_BASE 0x0C00
|
|
+#define UART_A_REGISTER_BASE 0x0C40
|
|
+#define UART_B_REGISTER_BASE 0x0C80
|
|
+#define GPS_CONTROL_REG_BASE 0x0CC0
|
|
+#define DMA_PORTC_CONTROL_REG_BASE 0x0D00
|
|
+#define DMA_PORTD_CONTROL_REG_BASE 0x0D00
|
|
+#define AES_RANDOM_DATA_BASE 0x0D80
|
|
+#define AES_KEY_IN_BASE 0x0D90
|
|
+#define RANDOM_DATA_LIB_BASE 0x0E00
|
|
+#define IR_DATA_BUFFER_BASE 0x0F00
|
|
+#define PORTA_TS_BUFFER_BASE 0x1000
|
|
+#define PORTA_I2S_BUFFER_BASE 0x1400
|
|
+#define PORTB_TS_BUFFER_BASE 0x1800
|
|
+#define PORTB_I2S_BUFFER_BASE 0x1C00
|
|
+
|
|
+/* -------- MSI control and state register -------- */
|
|
+#define MSI_DELAY_TIMER (MSI_CONTROL_REG_BASE + 0x00)
|
|
+#define MSI_INT_STATUS (MSI_CONTROL_REG_BASE + 0x08)
|
|
+#define MSI_INT_STATUS_CLR (MSI_CONTROL_REG_BASE + 0x0C)
|
|
+#define MSI_INT_STATUS_SET (MSI_CONTROL_REG_BASE + 0x10)
|
|
+#define MSI_INT_ENA (MSI_CONTROL_REG_BASE + 0x14)
|
|
+#define MSI_INT_ENA_CLR (MSI_CONTROL_REG_BASE + 0x18)
|
|
+#define MSI_INT_ENA_SET (MSI_CONTROL_REG_BASE + 0x1C)
|
|
+#define MSI_SOFT_RESET (MSI_CONTROL_REG_BASE + 0x20)
|
|
+#define MSI_CFG_SRC0 (MSI_CONTROL_REG_BASE + 0x24)
|
|
+
|
|
+/* -------- Hybird Controller System Control register -------- */
|
|
+#define MUX_MODE_CTRL (SYSTEM_CONTROL_REG_BASE + 0x00)
|
|
+ #define rbPaMSMask 0x07
|
|
+ #define rbPaMSDtvNoGpio 0x00 /*[2:0], DTV Simple (One Serial or Parallel) mode selected; (No GPIO)*/
|
|
+ #define rbPaMSDtv4bitGpio 0x01 /*[2:0], DTV TS2 Serial mode selected; (4bit GPIO)*/
|
|
+ #define rbPaMSDtv7bitGpio 0x02 /*[2:0], DTV TS0 Serial mode selected; (7bit GPIO)*/
|
|
+ #define rbPaMS8bitGpio 0x03 /*[2:0], GPIO mode selected; (8bit GPIO)*/
|
|
+ #define rbPaMSAtv 0x04 /*[2:0], 3'b1xx: ATV mode select*/
|
|
+
|
|
+ #define rbPbMSMask 0x38
|
|
+ #define rbPbMSDtvNoGpio 0x00 /*[5:3], DTV Simple (One Serial or Parallel) mode selected; (No GPIO)*/
|
|
+ #define rbPbMSDtv4bitGpio 0x08 /*[5:3], DTV TS2 Serial mode selected; (4bit GPIO)*/
|
|
+ #define rbPbMSDtv7bitGpio 0x10 /*[5:3], DTV TS0 Serial mode selected; (7bit GPIO)*/
|
|
+ #define rbPbMS8bitGpio 0x18 /*[5:3], GPIO mode selected; (8bit GPIO)*/
|
|
+ #define rbPbMSAtv 0x20 /*[5:3], 3'b1xx: ATV mode select*/
|
|
+
|
|
+ #define rbPaAESEN 0x40 /*[6], port A AES enable bit*/
|
|
+ #define rbPbAESEN 0x80 /*[7], port B AES enable bit*/
|
|
+
|
|
+#define INTERNAL_RST (SYSTEM_CONTROL_REG_BASE + 0x04)
|
|
+#define PERIPHERAL_CTRL (SYSTEM_CONTROL_REG_BASE + 0x08)
|
|
+#define GPIO_0to7_CTRL (SYSTEM_CONTROL_REG_BASE + 0x0C)
|
|
+#define GPIO_8to15_CTRL (SYSTEM_CONTROL_REG_BASE + 0x10)
|
|
+#define GPIO_16to24_CTRL (SYSTEM_CONTROL_REG_BASE + 0x14)
|
|
+#define GPIO_INT_SRC_CFG (SYSTEM_CONTROL_REG_BASE + 0x18)
|
|
+#define SYS_BUF_STATUS (SYSTEM_CONTROL_REG_BASE + 0x1C)
|
|
+#define PCIE_IP_REG_ACS (SYSTEM_CONTROL_REG_BASE + 0x20)
|
|
+#define PCIE_IP_REG_ACS_ADDR (SYSTEM_CONTROL_REG_BASE + 0x24)
|
|
+#define PCIE_IP_REG_ACS_DATA (SYSTEM_CONTROL_REG_BASE + 0x28)
|
|
+
|
|
+/* -------- IR Control register -------- */
|
|
+#define IR_Init_Reg (IR_CONTROL_REG_BASE + 0x00)
|
|
+#define IR_Idle_Cnt_Low (IR_CONTROL_REG_BASE + 0x04)
|
|
+#define IR_Idle_Cnt_High (IR_CONTROL_REG_BASE + 0x05)
|
|
+#define IR_Unit_Cnt_Low (IR_CONTROL_REG_BASE + 0x06)
|
|
+#define IR_Unit_Cnt_High (IR_CONTROL_REG_BASE + 0x07)
|
|
+#define IR_Data_Cnt (IR_CONTROL_REG_BASE + 0x08)
|
|
+
|
|
+#define rbIRen 0x80
|
|
+#define rbIRhighidle 0x10
|
|
+#define rbIRlowidle 0x00
|
|
+#define rbIRVld 0x04
|
|
+
|
|
+/* -------- I2C A control and state register -------- */
|
|
+#define I2C_A_CTL_STATUS (I2C_A_CONTROL_REG_BASE + 0x00)
|
|
+#define I2C_A_ADDR (I2C_A_CONTROL_REG_BASE + 0x04)
|
|
+#define I2C_A_SW_CTL (I2C_A_CONTROL_REG_BASE + 0x08)
|
|
+#define I2C_A_TIME_OUT_CNT (I2C_A_CONTROL_REG_BASE + 0x0C)
|
|
+#define I2C_A_FIFO_STATUS (I2C_A_CONTROL_REG_BASE + 0x10)
|
|
+#define I2C_A_FS_EN (I2C_A_CONTROL_REG_BASE + 0x14)
|
|
+#define I2C_A_FIFO_DATA (I2C_A_CONTROL_REG_BASE + 0x20)
|
|
+
|
|
+/* -------- I2C B control and state register -------- */
|
|
+#define I2C_B_CTL_STATUS (I2C_B_CONTROL_REG_BASE + 0x00)
|
|
+#define I2C_B_ADDR (I2C_B_CONTROL_REG_BASE + 0x04)
|
|
+#define I2C_B_SW_CTL (I2C_B_CONTROL_REG_BASE + 0x08)
|
|
+#define I2C_B_TIME_OUT_CNT (I2C_B_CONTROL_REG_BASE + 0x0C)
|
|
+#define I2C_B_FIFO_STATUS (I2C_B_CONTROL_REG_BASE + 0x10)
|
|
+#define I2C_B_FS_EN (I2C_B_CONTROL_REG_BASE + 0x14)
|
|
+#define I2C_B_FIFO_DATA (I2C_B_CONTROL_REG_BASE + 0x20)
|
|
+
|
|
+
|
|
+/* -------- GPS control register -------- */
|
|
+#define GPS_CTRL (GPS_CONTROL_REG_BASE + 0x00)
|
|
+#define VIDEO_CTRL_STATUS_A (ATV_PORTA_CONTROL_REG_BASE + 0x04)
|
|
+
|
|
+/* -------- Digital TV control register, Port A -------- */
|
|
+#define MPEG2_CTRL_A (DTV_PORTA_CONTROL_REG_BASE + 0x00)
|
|
+#define SERIAL_IN_ADDR_A (DTV_PORTA_CONTROL_REG_BASE + 0x4C)
|
|
+#define VLD_CNT_ADDR_A (DTV_PORTA_CONTROL_REG_BASE + 0x60)
|
|
+#define ERR_CNT_ADDR_A (DTV_PORTA_CONTROL_REG_BASE + 0x64)
|
|
+#define BRD_CNT_ADDR_A (DTV_PORTA_CONTROL_REG_BASE + 0x68)
|
|
+
|
|
+/* -------- AES control register, Port A -------- */
|
|
+#define AES_KEY_BASE_A (AES_PORTA_CONTROL_REG_BASE + 0x04)
|
|
+
|
|
+/* -------- DMA Control Register, Port A -------- */
|
|
+#define DMA_PORTA_CHAN0_ADDR_LOW (DMA_PORTA_CONTROL_REG_BASE + 0x00)
|
|
+#define DMA_PORTA_CHAN0_ADDR_HI (DMA_PORTA_CONTROL_REG_BASE + 0x04)
|
|
+#define DMA_PORTA_CHAN0_TRANS_STATE (DMA_PORTA_CONTROL_REG_BASE + 0x08)
|
|
+#define DMA_PORTA_CHAN0_CONTROL (DMA_PORTA_CONTROL_REG_BASE + 0x0C)
|
|
+#define DMA_PORTA_CHAN1_ADDR_LOW (DMA_PORTA_CONTROL_REG_BASE + 0x10)
|
|
+#define DMA_PORTA_CHAN1_ADDR_HI (DMA_PORTA_CONTROL_REG_BASE + 0x14)
|
|
+#define DMA_PORTA_CHAN1_TRANS_STATE (DMA_PORTA_CONTROL_REG_BASE + 0x18)
|
|
+#define DMA_PORTA_CHAN1_CONTROL (DMA_PORTA_CONTROL_REG_BASE + 0x1C)
|
|
+#define DMA_PORTA_MANAGEMENT (DMA_PORTA_CONTROL_REG_BASE + 0x20)
|
|
+#define VIDEO_CTRL_STATUS_B (ATV_PORTB_CONTROL_REG_BASE + 0x04)
|
|
+
|
|
+/* -------- Digital TV control register, Port B -------- */
|
|
+#define MPEG2_CTRL_B (DTV_PORTB_CONTROL_REG_BASE + 0x00)
|
|
+#define SERIAL_IN_ADDR_B (DTV_PORTB_CONTROL_REG_BASE + 0x4C)
|
|
+#define VLD_CNT_ADDR_B (DTV_PORTB_CONTROL_REG_BASE + 0x60)
|
|
+#define ERR_CNT_ADDR_B (DTV_PORTB_CONTROL_REG_BASE + 0x64)
|
|
+#define BRD_CNT_ADDR_B (DTV_PORTB_CONTROL_REG_BASE + 0x68)
|
|
+
|
|
+/* -------- AES control register, Port B -------- */
|
|
+#define AES_CTRL_B (AES_PORTB_CONTROL_REG_BASE + 0x00)
|
|
+#define AES_KEY_BASE_B (AES_PORTB_CONTROL_REG_BASE + 0x04)
|
|
+
|
|
+/* -------- DMA Control Register, Port B -------- */
|
|
+#define DMA_PORTB_CHAN0_ADDR_LOW (DMA_PORTB_CONTROL_REG_BASE + 0x00)
|
|
+#define DMA_PORTB_CHAN0_ADDR_HI (DMA_PORTB_CONTROL_REG_BASE + 0x04)
|
|
+#define DMA_PORTB_CHAN0_TRANS_STATE (DMA_PORTB_CONTROL_REG_BASE + 0x08)
|
|
+#define DMA_PORTB_CHAN0_CONTROL (DMA_PORTB_CONTROL_REG_BASE + 0x0C)
|
|
+#define DMA_PORTB_CHAN1_ADDR_LOW (DMA_PORTB_CONTROL_REG_BASE + 0x10)
|
|
+#define DMA_PORTB_CHAN1_ADDR_HI (DMA_PORTB_CONTROL_REG_BASE + 0x14)
|
|
+#define DMA_PORTB_CHAN1_TRANS_STATE (DMA_PORTB_CONTROL_REG_BASE + 0x18)
|
|
+#define DMA_PORTB_CHAN1_CONTROL (DMA_PORTB_CONTROL_REG_BASE + 0x1C)
|
|
+#define DMA_PORTB_MANAGEMENT (DMA_PORTB_CONTROL_REG_BASE + 0x20)
|
|
+
|
|
+#define DMA_TRANS_UNIT_188 (0x00000007)
|
|
+
|
|
+/* -------- Macro define of 24 interrupt resource --------*/
|
|
+#define DMA_A_CHAN0_DONE_INT (0x00000001)
|
|
+#define DMA_A_CHAN1_DONE_INT (0x00000002)
|
|
+#define DMA_B_CHAN0_DONE_INT (0x00000004)
|
|
+#define DMA_B_CHAN1_DONE_INT (0x00000008)
|
|
+#define DMA_C_CHAN0_DONE_INT (0x00000010)
|
|
+#define DMA_C_CHAN1_DONE_INT (0x00000020)
|
|
+#define DMA_D_CHAN0_DONE_INT (0x00000040)
|
|
+#define DMA_D_CHAN1_DONE_INT (0x00000080)
|
|
+#define DATA_BUF_OVERFLOW_INT (0x00000100)
|
|
+#define UART_0_X_INT (0x00000200)
|
|
+#define UART_1_X_INT (0x00000400)
|
|
+#define IR_X_INT (0x00000800)
|
|
+#define GPIO_0_INT (0x00001000)
|
|
+#define GPIO_1_INT (0x00002000)
|
|
+#define GPIO_2_INT (0x00004000)
|
|
+#define GPIO_3_INT (0x00008000)
|
|
+#define ALL_INT (0x0000FFFF)
|
|
+
|
|
+/* software I2C bit mask */
|
|
+#define SW_I2C_MSK_MODE 0x01
|
|
+#define SW_I2C_MSK_CLK_OUT 0x02
|
|
+#define SW_I2C_MSK_DAT_OUT 0x04
|
|
+#define SW_I2C_MSK_CLK_EN 0x08
|
|
+#define SW_I2C_MSK_DAT_EN 0x10
|
|
+#define SW_I2C_MSK_DAT_IN 0x40
|
|
+#define SW_I2C_MSK_CLK_IN 0x80
|
|
+
|
|
+#define SMI_VID 0x1ADE
|
|
+#define SMI_PID 0x3038
|
|
+#define SMI_TS_DMA_BUF_SIZE (1024 * 188)
|
|
+
|
|
+struct smi_cfg_info{
|
|
+ int type;
|
|
+#define SMI_DVBSKY_S952 0
|
|
+#define SMI_DVBSKY_S950 1
|
|
+#define SMI_DVBSKY_T9580 2
|
|
+#define SMI_DVBSKY_T982 3
|
|
+ char *name;
|
|
+ int ts_0;
|
|
+ int ts_1;
|
|
+ int fe_0;
|
|
+ int fe_1;
|
|
+#define DVBSKY_FE_NULL 0
|
|
+#define DVBSKY_FE_M88RS6000 1
|
|
+#define DVBSKY_FE_M88DS3103 2
|
|
+#define DVBSKY_FE_SIT2 3
|
|
+};
|
|
+
|
|
+struct smi_rc {
|
|
+ struct smi_dev *dev;
|
|
+ struct rc_dev *rc_dev;
|
|
+ char input_phys[64];
|
|
+ char input_name[64];
|
|
+
|
|
+ /*struct tasklet_struct tasklet;*/ /*tasklet_struct or work_struct ???*/
|
|
+ struct work_struct work;
|
|
+ u8 irData[256];
|
|
+
|
|
+ int users;
|
|
+};
|
|
+
|
|
+struct smi_port {
|
|
+ struct smi_dev *dev;
|
|
+ int idx;
|
|
+ int enable;
|
|
+ int fe_type;
|
|
+ /* regs */
|
|
+ u32 DMA_CHAN0_ADDR_LOW;
|
|
+ u32 DMA_CHAN0_ADDR_HI;
|
|
+ u32 DMA_CHAN0_TRANS_STATE;
|
|
+ u32 DMA_CHAN0_CONTROL;
|
|
+ u32 DMA_CHAN1_ADDR_LOW;
|
|
+ u32 DMA_CHAN1_ADDR_HI;
|
|
+ u32 DMA_CHAN1_TRANS_STATE;
|
|
+ u32 DMA_CHAN1_CONTROL;
|
|
+ u32 DMA_MANAGEMENT;
|
|
+ /* dma */
|
|
+ dma_addr_t dma_addr[2];
|
|
+ u8 *cpu_addr[2];
|
|
+ u32 _dmaInterruptCH0;
|
|
+ u32 _dmaInterruptCH1;
|
|
+ u32 _int_status;
|
|
+ struct tasklet_struct tasklet; /*tasklet_struct or work_struct ???*/
|
|
+ /* dvb */
|
|
+ struct dmx_frontend hw_frontend;
|
|
+ struct dmx_frontend mem_frontend;
|
|
+ struct dmxdev dmxdev;
|
|
+ struct dvb_adapter dvb_adapter;
|
|
+ struct dvb_demux demux;
|
|
+ struct dvb_net dvbnet;
|
|
+ int users;
|
|
+ struct dvb_frontend *fe;
|
|
+};
|
|
+
|
|
+struct smi_dev {
|
|
+ int nr;
|
|
+ struct smi_cfg_info *info;
|
|
+
|
|
+ /* pcie */
|
|
+ struct pci_dev *pci_dev;
|
|
+ u32 __iomem *lmmio;
|
|
+
|
|
+ /* ts port */
|
|
+ struct smi_port ts_port[2];
|
|
+
|
|
+ /* i2c */
|
|
+ struct i2c_adapter i2c_bus[2];
|
|
+ struct i2c_algo_bit_data i2c_bit[2];
|
|
+
|
|
+ /* ir */
|
|
+ struct smi_rc ir;
|
|
+};
|
|
+
|
|
+#define smi_read(reg) readl(dev->lmmio + ((reg)>>2))
|
|
+#define smi_write(reg, value) writel((value), dev->lmmio + ((reg)>>2))
|
|
+
|
|
+#define smi_andor(reg, mask, value) \
|
|
+ writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
|
|
+ ((value) & (mask)), dev->lmmio+((reg)>>2))
|
|
+
|
|
+#define smi_set(reg, bit) smi_andor((reg), (bit), (bit))
|
|
+#define smi_clear(reg, bit) smi_andor((reg), (bit), 0)
|
|
+
|
|
+#endif /* #ifndef _SMI_PCIE_H_ */
|
|
\ No newline at end of file
|
|
--
|
|
1.7.2.5
|
|
|