V4L/DVB (7448): Add support for Kworld ATSC 120

This board has a s5h1409 demod, plus a xc30x8 tuner (probably, xc3018).

This patch adds proper support for radio, video, s-video, composite and ATSC.
However, support for radio and video depends on having s5h1409 i2c gate open,
otherwise, xc30x8 chip won't be visible.

For a better support, some rework is needed on cx88 driver, to allow adding
xc30x8 to i2c bus without sending i2c 0 byte reading to 0xc2 address.

Thanks to Vanessa Ezekowitz <vanessaezekowitz@gmail.com> for helping to figure
out the proper parameters for s5h1409 and the GPIO pins used by each
configuration.

Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 6b83e34..70505b4 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -27,7 +27,6 @@
 
 #include "cx88.h"
 #include "tea5767.h"
-#include "tuner-xc2028.h"
 
 static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
 static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
@@ -1614,6 +1613,45 @@
 			.gpio2 = 0x0cfb,
 		},
 	},
+	/* Both radio, analog and ATSC work with this board.
+	   However, for analog to work, s5h1409 gate should be open,
+	   otherwise, tuner-xc3028 won't be detected.
+	   A proper fix require using the newer i2c methods to add
+	   tuner-xc3028 without doing an i2c probe.
+	 */
+	[CX88_BOARD_KWORLD_ATSC_120] = {
+		.name           = "Kworld PlusTV HD PCI 120 (ATSC 120)",
+		.tuner_type     = TUNER_XC2028,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.input          = { {
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x000000ff,
+			.gpio1  = 0x0000f35d,
+			.gpio2  = 0x00000000,
+		}, {
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x000000ff,
+			.gpio1  = 0x0000f37e,
+			.gpio2  = 0x00000000,
+		}, {
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x000000ff,
+			.gpio1  = 0x0000f37e,
+			.gpio2  = 0x00000000,
+		} },
+		.radio = {
+			.type   = CX88_RADIO,
+			.gpio0  = 0x000000ff,
+			.gpio1  = 0x0000f35d,
+			.gpio2  = 0x00000000,
+		},
+		.mpeg           = CX88_MPEG_DVB,
+	},
 };
 
 /* ------------------------------------------------------------------ */
@@ -1959,6 +1997,10 @@
 		.subvendor = 0x1554,
 		.subdevice = 0x4935,
 		.card      = CX88_BOARD_PROLINK_PV_8000GT,
+	}, {
+		.subvendor = 0x17de,
+		.subdevice = 0x08c1,
+		.card      = CX88_BOARD_KWORLD_ATSC_120,
 	},
 };
 
@@ -2200,6 +2242,7 @@
 	case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
 	case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
 	case CX88_BOARD_GENIATECH_X8000_MT:
+	case CX88_BOARD_KWORLD_ATSC_120:
 		return cx88_xc3028_geniatech_tuner_callback(core,
 							command, arg);
 	case CX88_BOARD_PROLINK_PV_8000GT:
@@ -2363,6 +2406,40 @@
 	}
 }
 
+/*
+ * Sets board-dependent xc3028 configuration
+ */
+void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl)
+{
+	memset(ctl, 0, sizeof(*ctl));
+
+	ctl->fname   = XC2028_DEFAULT_FIRMWARE;
+	ctl->max_len = 64;
+
+	switch (core->boardnr) {
+	case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
+		/* Doesn't work with firmware version 2.7 */
+		ctl->fname = "xc3028-v25.fw";
+		break;
+	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+		ctl->scode_table = XC3028_FE_ZARLINK456;
+		break;
+	case CX88_BOARD_KWORLD_ATSC_120:
+	case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+		ctl->demod = XC3028_FE_OREN538;
+		break;
+	case CX88_BOARD_PROLINK_PV_8000GT:
+		/*
+		 * This board uses non-MTS firmware
+		 */
+		break;
+	default:
+		ctl->demod = XC3028_FE_OREN538;
+		ctl->mts = 1;
+	}
+}
+EXPORT_SYMBOL_GPL(cx88_setup_xc3028);
+
 static void cx88_card_setup(struct cx88_core *core)
 {
 	static u8 eeprom[256];
@@ -2481,36 +2558,13 @@
 		struct v4l2_priv_tun_config  xc2028_cfg;
 		struct xc2028_ctrl           ctl;
 
+		/* Fills device-dependent initialization parameters */
+		cx88_setup_xc3028(core, &ctl);
+
+		/* Sends parameters to xc2028/3028 tuner */
 		memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
-		memset(&ctl, 0, sizeof(ctl));
-
-		ctl.fname   = XC2028_DEFAULT_FIRMWARE;
-		ctl.max_len = 64;
-
-		switch (core->boardnr) {
-		case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
-			/* Doesn't work with firmware version 2.7 */
-			ctl.fname = "xc3028-v25.fw";
-			break;
-		case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
-			ctl.scode_table = XC3028_FE_ZARLINK456;
-			break;
-		case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
-			ctl.demod = XC3028_FE_OREN538;
-			break;
-		case CX88_BOARD_PROLINK_PV_8000GT:
-			/*
-			 * This board uses non-MTS firmware
-			 */
-			break;
-		default:
-			ctl.demod = XC3028_FE_OREN538;
-			ctl.mts = 1;
-		}
-
 		xc2028_cfg.tuner = TUNER_XC2028;
 		xc2028_cfg.priv  = &ctl;
-
 		info_printk(core, "Asking xc2028/3028 to load firmware %s\n",
 			    ctl.fname);
 		cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &xc2028_cfg);
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 8dca20b..13cc395 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -45,7 +45,6 @@
 #include "nxt200x.h"
 #include "cx24123.h"
 #include "isl6421.h"
-#include "tuner-xc2028.h"
 #include "tuner-xc2028-types.h"
 #include "tuner-simple.h"
 #include "tda9887.h"
@@ -443,6 +442,16 @@
 	.mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
 };
 
+static struct s5h1409_config kworld_atsc_120_config = {
+	.demod_address = 0x32 >> 1,
+	.qam_if	       = 44000,
+	.output_mode   = S5H1409_SERIAL_OUTPUT,
+	.gpio	       = S5H1409_GPIO_OFF,
+	.inversion     = S5H1409_INVERSION_OFF,
+	.status_mode   = S5H1409_DEMODLOCKING,
+	.mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
 static struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
 	.i2c_address	= 0x64,
 	.if_khz		= 5380,
@@ -457,9 +466,12 @@
 static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
 {
 	struct dvb_frontend *fe;
+	struct xc2028_ctrl ctl;
 	struct xc2028_config cfg = {
 		.i2c_adap  = &dev->core->i2c_adap,
 		.i2c_addr  = addr,
+		.ctrl      = &ctl,
+		.callback  = cx88_tuner_callback,
 	};
 
 	if (!dev->dvb.frontend) {
@@ -469,6 +481,13 @@
 		return -EINVAL;
 	}
 
+	/*
+	 * Some xc3028 devices may be hidden by an I2C gate. This is known
+	 * to happen with some s5h1409-based devices.
+	 * Now that I2C gate is open, sets up xc3028 configuration
+	 */
+	cx88_setup_xc3028(dev->core, &ctl);
+
 	fe = dvb_attach(xc2028_attach, dev->dvb.frontend, &cfg);
 	if (!fe) {
 		printk(KERN_ERR "%s/2: xc3028 attach failed\n",
@@ -810,7 +829,7 @@
 			return -EINVAL;
 		break;
 	 case CX88_BOARD_GENIATECH_X8000_MT:
-	       dev->ts_gen_cntrl = 0x00;
+		dev->ts_gen_cntrl = 0x00;
 
 		dev->dvb.frontend = dvb_attach(zl10353_attach,
 					       &cx88_geniatech_x8000_mt,
@@ -818,6 +837,13 @@
 		if (attach_xc3028(0x61, dev) < 0)
 			return -EINVAL;
 		break;
+	 case CX88_BOARD_KWORLD_ATSC_120:
+		dev->dvb.frontend = dvb_attach(s5h1409_attach,
+					       &kworld_atsc_120_config,
+					       &dev->core->i2c_adap);
+		if (attach_xc3028(0x61, dev) < 0)
+			return -EINVAL;
+		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.h b/drivers/media/video/cx88/cx88.h
index c0f4912..d17ca71 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -37,6 +37,7 @@
 
 #include "btcx-risc.h"
 #include "cx88-reg.h"
+#include "tuner-xc2028.h"
 
 #include <linux/version.h>
 #include <linux/mutex.h>
@@ -219,6 +220,7 @@
 #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64
 #define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65
 #define CX88_BOARD_PROLINK_PV_8000GT       66
+#define CX88_BOARD_KWORLD_ATSC_120         67
 
 enum cx88_itype {
 	CX88_VMUX_COMPOSITE1 = 1,
@@ -603,6 +605,7 @@
 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);
+extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl);
 
 /* ----------------------------------------------------------- */
 /* cx88-tvaudio.c                                              */