[media] ddbridge: add I2C functions, add XO2 module support
authorDaniel Scheller <d.scheller@gmx.net>
Sun, 9 Apr 2017 19:38:26 +0000 (16:38 -0300)
committerMauro Carvalho Chehab <mchehab@s-opensource.com>
Tue, 20 Jun 2017 12:58:15 +0000 (09:58 -0300)
Some Flex modules (mostly with anyof C/C2/T/T2 demods based on the Sony
CXD28xxER series) are equipped with an interface named XO2 (which
appears to be the Lattice MachXO2). Add functionality to detect such
links and initialise them, so any tuner module with such an interface can
be used.

This also adds dummy detection for any possible connected module, telling
the user it isn't supported at this very moment.

Also adds i2c_io(), i2c_write() and i2c_write_reg(), all required for the
XO2 handling functionality.

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

index fef9932d071c4f919352a5f986d6b572d97d18f2..1ec49621349c797e515a4529398becf8ef499c6f 100644 (file)
 #include "stv0367_priv.h"
 #include "tda18212.h"
 
+static int xo2_speed = 2;
+module_param(xo2_speed, int, 0444);
+MODULE_PARM_DESC(xo2_speed, "default transfer speed for xo2 based duoflex, 0=55,1=75,2=90,3=104 MBit/s, default=2, use attribute to change for individual cards");
+
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 /* MSI had problems with lost interrupts, fixed but needs testing */
@@ -50,6 +54,24 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 /******************************************************************************/
 
+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_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
 {
        struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
@@ -83,6 +105,14 @@ static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
        return (i2c_transfer(adapter, msgs, 2) == 2) ? 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 ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
 {
        struct ddb *dev = i2c->dev;
@@ -1273,6 +1303,70 @@ static void ddb_ports_detach(struct ddb *dev)
 /****************************************************************************/
 /****************************************************************************/
 
+static int init_xo2(struct ddb_port *port)
+{
+       struct i2c_adapter *i2c = &port->i2c->adap;
+       u8 val, data[2];
+       int res;
+
+       res = i2c_read_regs(i2c, 0x10, 0x04, data, 2);
+       if (res < 0)
+               return res;
+
+       if (data[0] != 0x01)  {
+               pr_info("Port %d: invalid XO2\n", port->nr);
+               return -1;
+       }
+
+       i2c_read_reg(i2c, 0x10, 0x08, &val);
+       if (val != 0) {
+               i2c_write_reg(i2c, 0x10, 0x08, 0x00);
+               msleep(100);
+       }
+       /* Enable tuner power, disable pll, reset demods */
+       i2c_write_reg(i2c, 0x10, 0x08, 0x04);
+       usleep_range(2000, 3000);
+       /* Release demod resets */
+       i2c_write_reg(i2c, 0x10, 0x08, 0x07);
+
+       /* speed: 0=55,1=75,2=90,3=104 MBit/s */
+       i2c_write_reg(i2c, 0x10, 0x09,
+               ((xo2_speed >= 0 && xo2_speed <= 3) ? xo2_speed : 2));
+
+       i2c_write_reg(i2c, 0x10, 0x0a, 0x01);
+       i2c_write_reg(i2c, 0x10, 0x0b, 0x01);
+
+       usleep_range(2000, 3000);
+       /* Start XO2 PLL */
+       i2c_write_reg(i2c, 0x10, 0x08, 0x87);
+
+       return 0;
+}
+
+static int port_has_xo2(struct ddb_port *port, u8 *type, u8 *id)
+{
+       u8 probe[1] = { 0x00 }, data[4];
+
+       *type = DDB_XO2_TYPE_NONE;
+
+       if (i2c_io(&port->i2c->adap, 0x10, probe, 1, data, 4))
+               return 0;
+       if (data[0] == 'D' && data[1] == 'F') {
+               *id = data[2];
+               *type = DDB_XO2_TYPE_DUOFLEX;
+               return 1;
+       }
+       if (data[0] == 'C' && data[1] == 'I') {
+               *id = data[2];
+               *type = DDB_XO2_TYPE_CI;
+               return 1;
+       }
+       return 0;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+
 static int port_has_ci(struct ddb_port *port)
 {
        u8 val;
@@ -1323,6 +1417,7 @@ static void ddb_port_probe(struct ddb_port *port)
 {
        struct ddb *dev = port->dev;
        char *modname = "NO MODULE";
+       u8 xo2_type, xo2_id;
 
        port->class = DDB_PORT_NONE;
 
@@ -1330,6 +1425,58 @@ static void ddb_port_probe(struct ddb_port *port)
                modname = "CI";
                port->class = DDB_PORT_CI;
                ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+       } else if (port_has_xo2(port, &xo2_type, &xo2_id)) {
+               printk(KERN_INFO "Port %d (TAB %d): XO2 type: %d, id: %d\n",
+                       port->nr, port->nr+1, xo2_type, xo2_id);
+
+               ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+
+               switch (xo2_type) {
+               case DDB_XO2_TYPE_DUOFLEX:
+                       init_xo2(port);
+                       switch (xo2_id >> 2) {
+                       case 0:
+                               modname = "DUAL DVB-S2 (unsupported)";
+                               port->class = DDB_PORT_NONE;
+                               port->type = DDB_TUNER_XO2_DVBS_STV0910;
+                               break;
+                       case 1:
+                               modname = "DUAL DVB-C/T/T2 (unsupported)";
+                               port->class = DDB_PORT_NONE;
+                               port->type = DDB_TUNER_XO2_DVBCT2_SONY;
+                               break;
+                       case 2:
+                               modname = "DUAL DVB-ISDBT (unsupported)";
+                               port->class = DDB_PORT_NONE;
+                               port->type = DDB_TUNER_XO2_ISDBT_SONY;
+                               break;
+                       case 3:
+                               modname = "DUAL DVB-C/C2/T/T2 (unsupported)";
+                               port->class = DDB_PORT_NONE;
+                               port->type = DDB_TUNER_XO2_DVBC2T2_SONY;
+                               break;
+                       case 4:
+                               modname = "DUAL ATSC (unsupported)";
+                               port->class = DDB_PORT_NONE;
+                               port->type = DDB_TUNER_XO2_ATSC_ST;
+                               break;
+                       case 5:
+                               modname = "DUAL DVB-C/C2/T/T2/ISDBT (unsupported)";
+                               port->class = DDB_PORT_NONE;
+                               port->type = DDB_TUNER_XO2_DVBC2T2I_SONY;
+                               break;
+                       default:
+                               modname = "Unknown XO2 DuoFlex module\n";
+                               break;
+                       }
+                       break;
+               case DDB_XO2_TYPE_CI:
+                       printk(KERN_INFO "DuoFlex CI modules not supported\n");
+                       break;
+               default:
+                       printk(KERN_INFO "Unknown XO2 DuoFlex module\n");
+                       break;
+               }
        } else if (port_has_stv0900(port)) {
                modname = "DUAL DVB-S2";
                port->class = DDB_PORT_TUNER;
index 734e18eff1278efd8526eddcefb05a1291bbbc79..4e49faa7af80b0593abd736cc56c11c7a196b6b3 100644 (file)
 
 #define DDB_LINK_TAG(_x) (_x << DDB_LINK_SHIFT)
 
+#define DDB_XO2_TYPE_NONE      0
+#define DDB_XO2_TYPE_DUOFLEX   1
+#define DDB_XO2_TYPE_CI                2
+
 struct ddb_info {
        int   type;
 #define DDB_NONE         0
@@ -154,6 +158,13 @@ struct ddb_port {
 #define DDB_TUNER_DVBS_ST_AA    2
 #define DDB_TUNER_DVBCT_TR     16
 #define DDB_TUNER_DVBCT_ST     17
+#define DDB_TUNER_XO2_DVBS_STV0910     32
+#define DDB_TUNER_XO2_DVBCT2_SONY      33
+#define DDB_TUNER_XO2_ISDBT_SONY       34
+#define DDB_TUNER_XO2_DVBC2T2_SONY     35
+#define DDB_TUNER_XO2_ATSC_ST          36
+#define DDB_TUNER_XO2_DVBC2T2I_SONY    37
+
        u32                    adr;
 
        struct ddb_input      *input[2];