#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 };
                        .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,
+       },
 };
 
 /* ------------------------------------------------------------------ */
                .subvendor = 0x1554,
                .subdevice = 0x4935,
                .card      = CX88_BOARD_PROLINK_PV_8000GT,
+       }, {
+               .subvendor = 0x17de,
+               .subdevice = 0x08c1,
+               .card      = CX88_BOARD_KWORLD_ATSC_120,
        },
 };
 
        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:
        }
 }
 
+/*
+ * 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];
                struct v4l2_priv_tun_config  xc2028_cfg;
                struct xc2028_ctrl           ctl;
 
-               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;
-               }
+               /* Fills device-dependent initialization parameters */
+               cx88_setup_xc3028(core, &ctl);
 
+               /* Sends parameters to xc2028/3028 tuner */
+               memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
                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);
 
 #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"
        .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,
 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) {
                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",
                        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,
                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);