V4L/DVB (7043): New card supported(partially): Pinnacle 800i

From Zhang: This patch continues the support for the Pinnacle HD 800i.

Patch committed as-is, cleanups to follow ... Steve

Signed-off-by: Chaogui Zhang <czhang1974@gmail.com>
Signed-off-by: Steven Toth <stoth@hauppauge.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88
index 82ac825..bc5593b 100644
--- a/Documentation/video4linux/CARDLIST.cx88
+++ b/Documentation/video4linux/CARDLIST.cx88
@@ -56,3 +56,4 @@
  55 -> Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM  [c180:c980]
  56 -> Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder   [0070:9600,0070:9601,0070:9602]
  57 -> ADS Tech Instant Video PCI                          [1421:0390]
+ 58 -> Pinnacle PCTV HD 800i                               [11bd:0051]
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index d484e5c..3ba3157 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -1375,6 +1375,32 @@
 			.gpio0  = 0x07fa,
 		}},
 	},
+	[CX88_BOARD_PINNACLE_PCTV_HD_800i] = {
+		.name           = "Pinnacle PCTV HD 800i",
+		.tuner_type     = TUNER_XC5000,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x04fb,
+			.gpio1  = 0x10ff,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x04fb,
+			.gpio1  = 0x10ef,
+			.audioroute = 1,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x04fb,
+			.gpio1  = 0x10ef,
+			.audioroute = 1,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
 };
 
 /* ------------------------------------------------------------------ */
@@ -1684,6 +1710,10 @@
 		.subvendor = 0x1421,
 		.subdevice = 0x0390,
 		.card      = CX88_BOARD_ADSTECH_PTV_390,
+	},{
+		.subvendor = 0x11bd,
+		.subdevice = 0x0051,
+		.card      = CX88_BOARD_PINNACLE_PCTV_HD_800i,
 	},
 };
 
@@ -1851,6 +1881,39 @@
 }
 
 /* ----------------------------------------------------------------------- */
+/* Tuner callback function. Currently only needed for the Pinnacle 	   *
+ * PCTV HD 800i with an xc5000 sillicon tuner. This is used for both	   *
+ * analog tuner attach (tuner-core.c) and dvb tuner attach (cx88-dvb.c)    */
+
+int cx88_tuner_callback(void *i2c_algo, int command, int arg)
+{
+	struct i2c_algo_bit_data *algo = i2c_algo;
+	struct cx88_core *core = algo->data;
+
+	switch(core->boardnr) {
+	case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+		if(command == 0) { /* This is the reset command from xc5000 */
+			/* Reset XC5000 tuner via GPIO pin #2 */
+			cx_set(MO_GP0_IO, 0x00000400);
+			cx_clear(MO_GP0_IO, 0x00000004);
+			mdelay(200);
+			cx_set(MO_GP0_IO, 0x00000004);
+			printk(KERN_ERR "xc5000: in reset for xc5000\n");
+			mdelay(200);
+			return 0;
+		}
+		else {
+			printk(KERN_ERR
+				"xc5000: unknown tuner callback command.\n");
+			return -EINVAL;
+		}
+		break;
+	}
+	return 0; /* Should never be here */
+}
+EXPORT_SYMBOL(cx88_tuner_callback);
+
+/* ----------------------------------------------------------------------- */
 
 static void cx88_card_list(struct cx88_core *core, struct pci_dev *pci)
 {
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index fce19ca..0246f97 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -40,6 +40,8 @@
 #include "cx22702.h"
 #include "or51132.h"
 #include "lgdt330x.h"
+#include "s5h1409.h"
+#include "xc5000.h"
 #include "nxt200x.h"
 #include "cx24123.h"
 #include "isl6421.h"
@@ -371,6 +373,22 @@
 	.lnb_polarity  = 1,
 };
 
+static struct s5h1409_config pinnacle_pctv_hd_800i_config = {
+	.demod_address = 0x32 >> 1,
+	.output_mode   = S5H1409_PARALLEL_OUTPUT,
+	.gpio	       = S5H1409_GPIO_ON,
+	.qam_if	       = 44000,
+	.inversion     = S5H1409_INVERSION_OFF,
+	.status_mode   = S5H1409_DEMODLOCKING,
+};
+
+static struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
+	.i2c_address	= 0x64,
+	.if_khz		= 5380,
+	/* cannot set .video_dev here, do it right before attach */
+	.tuner_callback	= cx88_tuner_callback,
+};
+
 static int dvb_register(struct cx8802_dev *dev)
 {
 	/* init struct videobuf_dvb */
@@ -625,6 +643,24 @@
 			dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
 		}
 		break;
+	case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+		/* Parallel mpeg data port and punctured clock mode */
+		dev->ts_gen_cntrl = 0x04;
+
+		dev->dvb.frontend = dvb_attach(s5h1409_attach,
+					       &pinnacle_pctv_hd_800i_config,
+					       &dev->core->i2c_adap);
+		if (dev->dvb.frontend != NULL) {
+			/* tuner_config.video_dev must point to
+			 * i2c_adap.algo_data
+			 */
+			pinnacle_pctv_hd_800i_tuner_config.video_dev =
+						dev->core->i2c_adap.algo_data;
+			dvb_attach(xc5000_attach, dev->dvb.frontend,
+				   &dev->core->i2c_adap,
+				   &pinnacle_pctv_hd_800i_tuner_config);
+		}
+		break;
 	default:
 		printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
 		       dev->core->name);
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index 937497c..566b26a 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -109,20 +109,20 @@
 
 	if (core->board.radio_type != UNSET) {
 		if ((core->board.radio_addr==ADDR_UNSET)||(core->board.radio_addr==client->addr)) {
-			tun_setup.mode_mask = T_RADIO;
-			tun_setup.type = core->board.radio_type;
-			tun_setup.addr = core->board.radio_addr;
-
+			tun_setup.mode_mask	 = T_RADIO;
+			tun_setup.type		 = core->board.radio_type;
+			tun_setup.addr		 = core->board.radio_addr;
+			tun_setup.tuner_callback = cx88_tuner_callback;
 			client->driver->command (client, TUNER_SET_TYPE_ADDR, &tun_setup);
 		}
 	}
 	if (core->board.tuner_type != UNSET) {
 		if ((core->board.tuner_addr==ADDR_UNSET)||(core->board.tuner_addr==client->addr)) {
 
-			tun_setup.mode_mask = T_ANALOG_TV;
-			tun_setup.type = core->board.tuner_type;
-			tun_setup.addr = core->board.tuner_addr;
-
+			tun_setup.mode_mask	 = T_ANALOG_TV;
+			tun_setup.type		 = core->board.tuner_type;
+			tun_setup.addr		 = core->board.tuner_addr;
+			tun_setup.tuner_callback = cx88_tuner_callback;
 			client->driver->command (client,TUNER_SET_TYPE_ADDR, &tun_setup);
 		}
 	}
@@ -182,6 +182,7 @@
 	[ 0xa0 >> 1 ] = "eeprom",
 	[ 0xc0 >> 1 ] = "tuner (analog)",
 	[ 0xc2 >> 1 ] = "tuner (analog/dvb)",
+	[ 0xc8 >> 1 ] = "xc5000",
 };
 
 static void do_i2c_scan(char *name, struct i2c_client *c)
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 339a88a..a21b503 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -102,7 +102,7 @@
 		cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
 		udelay(100);
 		cx_write(MO_PINMUX_IO, 0x00);
-		cx_write(TS_HW_SOP_CNTRL,0x47<<16|188<<4|0x01);
+		cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01);
 		switch (core->boardnr) {
 		case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
 		case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
@@ -117,6 +117,11 @@
 			break;
 		case CX88_BOARD_HAUPPAUGE_HVR1300:
 			break;
+		case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+			/* Enable MPEG parallel port */
+			cx_write(MO_PINMUX_IO, 0x80);
+			udelay(100);
+			break;
 		default:
 			cx_write(TS_SOP_STAT, 0x00);
 			break;
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index 0cd2946..4e823f2 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -210,6 +210,7 @@
 #define CX88_BOARD_TE_DTV_250_OEM_SWANN    55
 #define CX88_BOARD_HAUPPAUGE_HVR1300       56
 #define CX88_BOARD_ADSTECH_PTV_390         57
+#define CX88_BOARD_PINNACLE_PCTV_HD_800i   58
 
 enum cx88_itype {
 	CX88_VMUX_COMPOSITE1 = 1,
@@ -589,6 +590,7 @@
 /* ----------------------------------------------------------- */
 /* cx88-cards.c                                                */
 
+extern int cx88_tuner_callback(void *dev, int command, int arg);
 extern int cx88_get_resources(const struct cx88_core *core,
 			      struct pci_dev *pci);
 extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr);