media: ngene: add XO2 module support
authorDaniel Scheller <d.scheller@gmx.net>
Sun, 25 Feb 2018 12:31:33 +0000 (07:31 -0500)
committerMauro Carvalho Chehab <mchehab@s-opensource.com>
Tue, 6 Mar 2018 16:01:58 +0000 (11:01 -0500)
Detect and initialise modules equipped with XO2 interfaces (Lattice
MachXO2). This requires a few more I2C transfer functions which this adds
as well. Defines for the different possible (available) module types are
added to ngene.h. The support for the actual tuners contained on these
addon modules is kept separate from this commit and is being added with
the next commits.

The xo2names array is temporarily marked __maybe_unused to silence a
corresponding compiler warning at this stage.

Signed-off-by: Daniel Scheller <d.scheller@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
drivers/media/pci/ngene/ngene-cards.c
drivers/media/pci/ngene/ngene.h

index 7ec5f68b1ec7bb6944026063b1beb40bb3726f1a..05b8e56999ecc491ec004a703e31a6d38087c8fa 100644 (file)
 /* I2C transfer functions used for demod/tuner probing***********************/
 /****************************************************************************/
 
+static int i2c_io(struct i2c_adapter *adapter, u8 adr,
+                 u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
+{
+       struct i2c_msg msgs[2] = {{.addr = adr,  .flags = 0,
+                                  .buf  = wbuf, .len   = wlen },
+                                 {.addr = adr,  .flags = I2C_M_RD,
+                                  .buf  = rbuf,  .len   = rlen } };
+       return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
+{
+       struct i2c_msg msg = {.addr = adr, .flags = 0,
+                             .buf = data, .len = len};
+
+       return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_write_reg(struct i2c_adapter *adap, u8 adr,
+                        u8 reg, u8 val)
+{
+       u8 msg[2] = {reg, val};
+
+       return i2c_write(adap, adr, msg, 2);
+}
+
 static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
 {
        struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
@@ -78,6 +104,12 @@ static int i2c_read_regs(struct i2c_adapter *adapter,
 
        return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
 }
+
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
+{
+       return i2c_read_regs(adapter, adr, reg, val, 1);
+}
+
 /****************************************************************************/
 /* Demod/tuner attachment ***************************************************/
 /****************************************************************************/
@@ -390,12 +422,98 @@ static int demod_attach_drxk(struct ngene_channel *chan,
        return 0;
 }
 
+/****************************************************************************/
+/* XO2 related lists and functions ******************************************/
+/****************************************************************************/
+
+static char __maybe_unused *xo2names[] = {
+       "DUAL DVB-S2",
+       "DUAL DVB-C/T/T2",
+       "DUAL DVB-ISDBT",
+       "DUAL DVB-C/C2/T/T2",
+       "DUAL ATSC",
+       "DUAL DVB-C/C2/T/T2/I",
+};
+
+static int init_xo2(struct ngene_channel *chan, struct i2c_adapter *i2c)
+{
+       struct device *pdev = &chan->dev->pci_dev->dev;
+       u8 addr = 0x10;
+       u8 val, data[2];
+       int res;
+
+       res = i2c_read_regs(i2c, addr, 0x04, data, 2);
+       if (res < 0)
+               return res;
+
+       if (data[0] != 0x01)  {
+               dev_info(pdev, "Invalid XO2 on channel %d\n", chan->number);
+               return -1;
+       }
+
+       i2c_read_reg(i2c, addr, 0x08, &val);
+       if (val != 0) {
+               i2c_write_reg(i2c, addr, 0x08, 0x00);
+               msleep(100);
+       }
+       /* Enable tuner power, disable pll, reset demods */
+       i2c_write_reg(i2c, addr, 0x08, 0x04);
+       usleep_range(2000, 3000);
+       /* Release demod resets */
+       i2c_write_reg(i2c, addr, 0x08, 0x07);
+
+       /*
+        * speed: 0=55,1=75,2=90,3=104 MBit/s
+        * Note: The ngene hardware must be run at 75 MBit/s compared
+        * to more modern ddbridge hardware which runs at 90 MBit/s,
+        * else there will be issues with the data transport and non-
+        * working secondary/slave demods/tuners.
+        */
+       i2c_write_reg(i2c, addr, 0x09, 1);
+
+       i2c_write_reg(i2c, addr, 0x0a, 0x01);
+       i2c_write_reg(i2c, addr, 0x0b, 0x01);
+
+       usleep_range(2000, 3000);
+       /* Start XO2 PLL */
+       i2c_write_reg(i2c, addr, 0x08, 0x87);
+
+       return 0;
+}
+
+static int port_has_xo2(struct i2c_adapter *i2c, u8 *type, u8 *id)
+{
+       u8 probe[1] = { 0x00 }, data[4];
+       u8 addr = 0x10;
+
+       *type = NGENE_XO2_TYPE_NONE;
+
+       if (i2c_io(i2c, addr, probe, 1, data, 4))
+               return 0;
+       if (data[0] == 'D' && data[1] == 'F') {
+               *id = data[2];
+               *type = NGENE_XO2_TYPE_DUOFLEX;
+               return 1;
+       }
+       if (data[0] == 'C' && data[1] == 'I') {
+               *id = data[2];
+               *type = NGENE_XO2_TYPE_CI;
+               return 1;
+       }
+       return 0;
+}
+
+/****************************************************************************/
+/* Probing and port/channel handling ****************************************/
+/****************************************************************************/
+
 static int cineS2_probe(struct ngene_channel *chan)
 {
        struct device *pdev = &chan->dev->pci_dev->dev;
        struct i2c_adapter *i2c;
        struct stv090x_config *fe_conf;
        u8 buf[3];
+       u8 xo2_type, xo2_id;
        struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
        int rc;
 
@@ -405,7 +523,31 @@ static int cineS2_probe(struct ngene_channel *chan)
        else
                i2c = &chan->dev->channel[1].i2c_adapter;
 
-       if (port_has_stv0900(i2c, chan->number)) {
+       if (port_has_xo2(i2c, &xo2_type, &xo2_id)) {
+               xo2_id >>= 2;
+               dev_dbg(pdev, "XO2 on channel %d (type %d, id %d)\n",
+                       chan->number, xo2_type, xo2_id);
+
+               switch (xo2_type) {
+               case NGENE_XO2_TYPE_DUOFLEX:
+                       if (chan->number & 1)
+                               dev_dbg(pdev,
+                                       "skipping XO2 init on odd channel %d",
+                                       chan->number);
+                       else
+                               init_xo2(chan, i2c);
+
+                       /* TODO: implement support for XO2 module types */
+                       dev_warn(pdev, "XO2 not supported\n");
+                       return -ENODEV;
+               case NGENE_XO2_TYPE_CI:
+                       dev_info(pdev, "DuoFlex CI modules not supported\n");
+                       return -ENODEV;
+               default:
+                       dev_info(pdev, "Unsupported XO2 module type\n");
+                       return -ENODEV;
+               }
+       } else if (port_has_stv0900(i2c, chan->number)) {
                chan->demod_type = DEMOD_TYPE_STV090X;
                fe_conf = chan->dev->card_info->fe_config[chan->number];
                /* demod found, attach it */
index 1b88a9aa7aac822dbe3596fd6c96081fd7ec249d..72195f6552b3a4cb99789b9f0046cee3b56f7117 100644 (file)
 #define DEMOD_TYPE_DRXK                1
 #define DEMOD_TYPE_STV0367     2
 
+#define DEMOD_TYPE_XO2         32
+#define DEMOD_TYPE_STV0910     (DEMOD_TYPE_XO2 + 0)
+#define DEMOD_TYPE_SONY_CT2    (DEMOD_TYPE_XO2 + 1)
+#define DEMOD_TYPE_SONY_ISDBT  (DEMOD_TYPE_XO2 + 2)
+#define DEMOD_TYPE_SONY_C2T2   (DEMOD_TYPE_XO2 + 3)
+#define DEMOD_TYPE_ST_ATSC     (DEMOD_TYPE_XO2 + 4)
+#define DEMOD_TYPE_SONY_C2T2I  (DEMOD_TYPE_XO2 + 5)
+
+#define NGENE_XO2_TYPE_NONE    0
+#define NGENE_XO2_TYPE_DUOFLEX 1
+#define NGENE_XO2_TYPE_CI      2
+
 enum STREAM {
        STREAM_VIDEOIN1 = 0,        /* ITU656 or TS Input */
        STREAM_VIDEOIN2,