diff --git a/packages/linux/patches/3.16/linux-211-dvbsky-s950v3-s952v3.patch b/packages/linux/patches/3.16/linux-211-dvbsky-s950v3-s952v3.patch new file mode 100644 index 0000000000..9505198ab9 --- /dev/null +++ b/packages/linux/patches/3.16/linux-211-dvbsky-s950v3-s952v3.patch @@ -0,0 +1,3484 @@ +From d89d7ff6fd6c064cfae0100b0a06df1340c87a55 Mon Sep 17 00:00:00 2001 +From: Stefan Saraev +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 ++ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++ */ ++ ++#ifndef DVBSKY_M88RS6000_H ++#define DVBSKY_M88RS6000_H ++ ++#include ++#include ++ ++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 ++ ++ */ ++ ++#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 ++ ++*/ ++#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, 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 ++ ++ */ ++#ifndef _SMI_PCIE_H_ ++#define _SMI_PCIE_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 + diff --git a/projects/Generic/linux/linux.i386.conf b/projects/Generic/linux/linux.i386.conf index a69fddcd29..5954a603ef 100644 --- a/projects/Generic/linux/linux.i386.conf +++ b/projects/Generic/linux/linux.i386.conf @@ -2477,6 +2477,7 @@ CONFIG_DVB_MANTIS=m # CONFIG_DVB_HOPPER is not set CONFIG_DVB_NGENE=m CONFIG_DVB_DDBRIDGE=m +CONFIG_DVB_SMIPCIE=m # CONFIG_V4L_PLATFORM_DRIVERS is not set # CONFIG_V4L_MEM2MEM_DRIVERS is not set # CONFIG_V4L_TEST_DRIVERS is not set @@ -2647,6 +2648,7 @@ CONFIG_DVB_CX24116=m CONFIG_DVB_CX24117=m CONFIG_DVB_DVBSKY_M88DS3103=m CONFIG_DVB_DVBSKY_M88DC2800=m +CONFIG_DVB_DVBSKY_M88RS6000=m CONFIG_DVB_SI21XX=m CONFIG_DVB_TS2020=m CONFIG_DVB_DS3000=m diff --git a/projects/Generic/linux/linux.x86_64.conf b/projects/Generic/linux/linux.x86_64.conf index fc0e405c2a..53f8950cdf 100644 --- a/projects/Generic/linux/linux.x86_64.conf +++ b/projects/Generic/linux/linux.x86_64.conf @@ -2457,6 +2457,7 @@ CONFIG_DVB_MANTIS=m # CONFIG_DVB_HOPPER is not set CONFIG_DVB_NGENE=m CONFIG_DVB_DDBRIDGE=m +CONFIG_DVB_SMIPCIE=m # CONFIG_V4L_PLATFORM_DRIVERS is not set # CONFIG_V4L_MEM2MEM_DRIVERS is not set # CONFIG_V4L_TEST_DRIVERS is not set @@ -2627,6 +2628,7 @@ CONFIG_DVB_CX24116=m CONFIG_DVB_CX24117=m CONFIG_DVB_DVBSKY_M88DS3103=m CONFIG_DVB_DVBSKY_M88DC2800=m +CONFIG_DVB_DVBSKY_M88RS6000=m CONFIG_DVB_SI21XX=m CONFIG_DVB_TS2020=m CONFIG_DVB_DS3000=m