V4L/DVB (10805): Add support for NetUP Dual DVB-S2 CI card

Add support for NetUP Dual DVB-S2 CI card
The card based on cx23885 PCI-e bridge, CiMax SP2 Common Interface chips,
STM lnbh24 LNB power chip, stv6110 tuners and stv0900 demodulator.
http://www.linuxtv.org/wiki/index.php/NetUP_Dual_DVB_S2_CI

Signed-off-by: Igor M. Liplianin <liplianin@netup.ru>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
index 00f1e2e..b62f16d 100644
--- a/drivers/media/video/cx23885/Kconfig
+++ b/drivers/media/video/cx23885/Kconfig
@@ -16,6 +16,7 @@
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
 	select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
+	select DVB_LNBP21 if !DVB_FE_CUSTOMIZE
 	select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMIZE
 	select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE
 	select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
index 29c23b4..ab8ea35 100644
--- a/drivers/media/video/cx23885/Makefile
+++ b/drivers/media/video/cx23885/Makefile
@@ -1,4 +1,6 @@
-cx23885-objs	:= cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o
+cx23885-objs	:= cx23885-cards.o cx23885-video.o cx23885-vbi.o \
+		    cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
+		    netup-init.o cimax2.o netup-eeprom.o
 
 obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
 
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 7ff339a..08cd793 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -27,6 +27,7 @@
 
 #include "cx23885.h"
 #include "tuner-xc2028.h"
+#include "netup-init.h"
 
 /* ------------------------------------------------------------------ */
 /* board config info                                                  */
@@ -174,6 +175,12 @@
 		.name		= "DVBWorld DVB-S2 2005",
 		.portb		= CX23885_MPEG_DVB,
 	},
+	[CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = {
+		.cimax		= 1,
+		.name		= "NetUP Dual DVB-S2 CI",
+		.portb		= CX23885_MPEG_DVB,
+		.portc		= CX23885_MPEG_DVB,
+	},
 };
 const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
 
@@ -269,6 +276,10 @@
 		.subvendor = 0x0001,
 		.subdevice = 0x2005,
 		.card      = CX23885_BOARD_DVBWORLD_2005,
+	}, {
+		.subvendor = 0x1b55,
+		.subdevice = 0x2a2c,
+		.card      = CX23885_BOARD_NETUP_DUAL_DVBS2_CI,
 	},
 };
 const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -582,6 +593,32 @@
 		cx_write(MC417_OEN, 0x00001000);
 		cx_write(MC417_RWD, 0x00001800);
 		break;
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+		/* GPIO-0 INTA from CiMax1
+		   GPIO-1 INTB from CiMax2
+		   GPIO-2 reset chips
+		   GPIO-3 to GPIO-10 data/addr for CA
+		   GPIO-11 ~CS0 to CiMax1
+		   GPIO-12 ~CS1 to CiMax2
+		   GPIO-13 ADL0 load LSB addr
+		   GPIO-14 ADL1 load MSB addr
+		   GPIO-15 ~RDY from CiMax
+		   GPIO-17 ~RD to CiMax
+		   GPIO-18 ~WR to CiMax
+		 */
+		cx_set(GP0_IO, 0x00040000); /* GPIO as out */
+		/* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */
+		cx_clear(GP0_IO, 0x00030004);
+		mdelay(100);/* reset delay */
+		cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */
+		cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */
+		/* GPIO-15 IN as ~ACK, rest as OUT */
+		cx_write(MC417_OEN, 0x00001000);
+		/* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */
+		cx_write(MC417_RWD, 0x0000c300);
+		/* enable irq */
+		cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
+		break;
 	}
 }
 
@@ -669,6 +706,14 @@
 		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
 		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
 		break;
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+		ts1->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+		ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+		ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500:
 	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
@@ -693,9 +738,17 @@
 	case CX23885_BOARD_HAUPPAUGE_HVR1700:
 	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
 	case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
 		request_module("cx25840");
 		break;
 	}
+
+	/* AUX-PLL 27MHz CLK */
+	switch (dev->board) {
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+		netup_initialize(dev);
+		break;
+	}
 }
 
 /* ------------------------------------------------------------------ */
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index 8f6fb2a..1b40145 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -31,6 +31,7 @@
 #include <asm/div64.h>
 
 #include "cx23885.h"
+#include "cimax2.h"
 
 MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
 MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
@@ -791,6 +792,8 @@
 	dev->pci_bus  = dev->pci->bus->number;
 	dev->pci_slot = PCI_SLOT(dev->pci->devfn);
 	dev->pci_irqmask = 0x001f00;
+	if (cx23885_boards[dev->board].cimax > 0)
+		dev->pci_irqmask |= 0x01800000; /* for CiMaxes */
 
 	/* External Master 1 Bus */
 	dev->i2c_bus[0].nr = 0;
@@ -1643,7 +1646,9 @@
 	    (pci_status & PCI_MSK_VID_B) ||
 	    (pci_status & PCI_MSK_VID_A) ||
 	    (pci_status & PCI_MSK_AUD_INT) ||
-	    (pci_status & PCI_MSK_AUD_EXT)) {
+	    (pci_status & PCI_MSK_AUD_EXT) ||
+	    (pci_status & PCI_MSK_GPIO0) ||
+	    (pci_status & PCI_MSK_GPIO1)) {
 
 		if (pci_status & PCI_MSK_RISC_RD)
 			dprintk(7, " (PCI_MSK_RISC_RD   0x%08x)\n",
@@ -1685,8 +1690,19 @@
 			dprintk(7, " (PCI_MSK_AUD_EXT   0x%08x)\n",
 				PCI_MSK_AUD_EXT);
 
+		if (pci_status & PCI_MSK_GPIO0)
+			dprintk(7, " (PCI_MSK_GPIO0     0x%08x)\n",
+				PCI_MSK_GPIO0);
+
+		if (pci_status & PCI_MSK_GPIO1)
+			dprintk(7, " (PCI_MSK_GPIO1     0x%08x)\n",
+				PCI_MSK_GPIO1);
 	}
 
+	if ((pci_status & PCI_MSK_GPIO0) || (pci_status & PCI_MSK_GPIO1))
+		/* handled += cx23885_irq_gpio(dev, pci_status); */
+		handled += netup_ci_slot_status(dev, pci_status);
+
 	if (ts1_status) {
 		if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
 			handled += cx23885_irq_ts(ts1, ts1_status);
@@ -1759,6 +1775,8 @@
 	}
 
 	pci_set_drvdata(pci_dev, dev);
+	cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */
+
 	return 0;
 
 fail_irq:
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 14a6540..9a0bc6e 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -30,6 +30,7 @@
 #include "cx23885.h"
 #include <media/v4l2-common.h>
 
+#include "dvb_ca_en50221.h"
 #include "s5h1409.h"
 #include "s5h1411.h"
 #include "mt2131.h"
@@ -43,7 +44,13 @@
 #include "dib7000p.h"
 #include "dibx000_common.h"
 #include "zl10353.h"
+#include "stv0900.h"
+#include "stv6110.h"
+#include "lnbh24.h"
 #include "cx24116.h"
+#include "cimax2.h"
+#include "netup-eeprom.h"
+#include "netup-init.h"
 
 static unsigned int debug;
 
@@ -309,6 +316,31 @@
 	.no_tuner      = 1,
 };
 
+static struct stv0900_config netup_stv0900_config = {
+	.demod_address = 0x68,
+	.xtal = 27000000,
+	.clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
+	.diseqc_mode = 2,/* 2/3 PWM */
+	.path1_mode = 2,/*Serial continues clock */
+	.path2_mode = 2,/*Serial continues clock */
+	.tun1_maddress = 0,/* 0x60 */
+	.tun2_maddress = 3,/* 0x63 */
+	.tun1_adc = 1,/* 1 Vpp */
+	.tun2_adc = 1,/* 1 Vpp */
+};
+
+static struct stv6110_config netup_stv6110_tunerconfig_a = {
+	.i2c_address = 0x60,
+	.mclk = 27000000,
+	.iq_wiring = 0,
+};
+
+static struct stv6110_config netup_stv6110_tunerconfig_b = {
+	.i2c_address = 0x63,
+	.mclk = 27000000,
+	.iq_wiring = 1,
+};
+
 static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
 {
 	struct cx23885_tsport *port = fe->dvb->priv;
@@ -340,6 +372,7 @@
 	struct cx23885_dev *dev = port->dev;
 	struct cx23885_i2c *i2c_bus = NULL;
 	struct videobuf_dvb_frontend *fe0;
+	int ret;
 
 	/* Get the first frontend */
 	fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
@@ -580,6 +613,51 @@
 			&dvbworld_cx24116_config,
 			&i2c_bus->i2c_adap);
 		break;
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+		i2c_bus = &dev->i2c_bus[0];
+		switch (port->nr) {
+		/* port B */
+		case 1:
+			fe0->dvb.frontend = dvb_attach(stv0900_attach,
+							&netup_stv0900_config,
+							&i2c_bus->i2c_adap, 0);
+			if (fe0->dvb.frontend != NULL) {
+				if (dvb_attach(stv6110_attach,
+						fe0->dvb.frontend,
+						&netup_stv6110_tunerconfig_a,
+						&i2c_bus->i2c_adap)) {
+					if (!dvb_attach(lnbh24_attach,
+							fe0->dvb.frontend,
+							&i2c_bus->i2c_adap,
+							LNBH24_PCL, 0, 0x09))
+						printk(KERN_ERR
+							"No LNBH24 found!\n");
+
+				}
+			}
+			break;
+		/* port C */
+		case 2:
+			fe0->dvb.frontend = dvb_attach(stv0900_attach,
+							&netup_stv0900_config,
+							&i2c_bus->i2c_adap, 1);
+			if (fe0->dvb.frontend != NULL) {
+				if (dvb_attach(stv6110_attach,
+						fe0->dvb.frontend,
+						&netup_stv6110_tunerconfig_b,
+						&i2c_bus->i2c_adap)) {
+					if (!dvb_attach(lnbh24_attach,
+							fe0->dvb.frontend,
+							&i2c_bus->i2c_adap,
+							LNBH24_PCL, 0, 0x0a))
+						printk(KERN_ERR
+							"No LNBH24 found!\n");
+
+				}
+			}
+			break;
+		}
+		break;
 	default:
 		printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
 			" isn't supported yet\n",
@@ -601,9 +679,33 @@
 		fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
 
 	/* register everything */
-	return videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
+	ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
 		&dev->pci->dev, adapter_nr, 0);
 
+	/* init CI & MAC */
+	switch (dev->board) {
+	case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: {
+		static struct netup_card_info cinfo;
+
+		netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
+		memcpy(port->frontends.adapter.proposed_mac,
+				cinfo.port[port->nr - 1].mac, 6);
+		printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC="
+			"%02X:%02X:%02X:%02X:%02X:%02X\n",
+			port->nr,
+			port->frontends.adapter.proposed_mac[0],
+			port->frontends.adapter.proposed_mac[1],
+			port->frontends.adapter.proposed_mac[2],
+			port->frontends.adapter.proposed_mac[3],
+			port->frontends.adapter.proposed_mac[4],
+			port->frontends.adapter.proposed_mac[5]);
+
+		netup_ci_init(port);
+		break;
+		}
+	}
+
+	return ret;
 }
 
 int cx23885_dvb_register(struct cx23885_tsport *port)
@@ -676,6 +778,8 @@
 	if (fe0->dvb.frontend)
 		videobuf_dvb_unregister_bus(&port->frontends);
 
+	netup_ci_exit(port);
+
 	return 0;
 }
 
diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h
index 20b68a2..eafbe52 100644
--- a/drivers/media/video/cx23885/cx23885-reg.h
+++ b/drivers/media/video/cx23885/cx23885-reg.h
@@ -212,6 +212,8 @@
 
 #define DEV_CNTRL2	0x00040000
 
+#define PCI_MSK_GPIO1   (1 << 24)
+#define PCI_MSK_GPIO0   (1 << 23)
 #define PCI_MSK_APB_DMA   (1 << 12)
 #define PCI_MSK_AL_WR     (1 << 11)
 #define PCI_MSK_AL_RD     (1 << 10)
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index 37a88b1..779fc35 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -70,6 +70,7 @@
 #define CX23885_BOARD_TBS_6920                 14
 #define CX23885_BOARD_TEVII_S470               15
 #define CX23885_BOARD_DVBWORLD_2005            16
+#define CX23885_BOARD_NETUP_DUAL_DVBS2_CI      17
 
 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
 #define CX23885_NORMS (\
@@ -187,6 +188,7 @@
 	 */
 	u32			clk_freq;
 	struct cx23885_input    input[MAX_CX23885_INPUT];
+	int			cimax; /* for NetUP */
 };
 
 struct cx23885_subid {
@@ -269,6 +271,7 @@
 
 	/* Allow a single tsport to have multiple frontends */
 	u32                        num_frontends;
+	void                       *port_priv;
 };
 
 struct cx23885_dev {