Message ID | 20170409193828.18458-18-d.scheller.oss@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
not related to cxd2841er. I'm skipping this ... same for rest of the patches (18/19, 19/19). 2017-04-09 15:38 GMT-04:00 Daniel Scheller <d.scheller.oss@gmail.com>: > From: Daniel Scheller <d.scheller@gmx.net> > > 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> > --- > drivers/media/pci/ddbridge/ddbridge-core.c | 147 +++++++++++++++++++++++++++++ > drivers/media/pci/ddbridge/ddbridge.h | 11 +++ > 2 files changed, 158 insertions(+) > > diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c > index 6b49fa9..ab88fcf 100644 > --- a/drivers/media/pci/ddbridge/ddbridge-core.c > +++ b/drivers/media/pci/ddbridge/ddbridge-core.c > @@ -43,6 +43,10 @@ > #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; > @@ -1272,6 +1302,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; > @@ -1322,6 +1416,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; > > @@ -1329,6 +1424,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; > diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h > index 734e18e..4e49faa 100644 > --- a/drivers/media/pci/ddbridge/ddbridge.h > +++ b/drivers/media/pci/ddbridge/ddbridge.h > @@ -48,6 +48,10 @@ > > #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]; > -- > 2.10.2 >
diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 6b49fa9..ab88fcf 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -43,6 +43,10 @@ #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; @@ -1272,6 +1302,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; @@ -1322,6 +1416,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; @@ -1329,6 +1424,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; diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index 734e18e..4e49faa 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -48,6 +48,10 @@ #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];