[media] ngene: add support for Terratec Cynergy 2400i Dual DVB-T
This code is based on ngene initial check-in (dae52d009f
)
Signed-off-by: Patrice Chotard <patricechotard@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
5fb67074c6
commit
397e972350
|
@ -42,6 +42,8 @@
|
||||||
#include "mt2131.h"
|
#include "mt2131.h"
|
||||||
#include "tda18271c2dd.h"
|
#include "tda18271c2dd.h"
|
||||||
#include "drxk.h"
|
#include "drxk.h"
|
||||||
|
#include "drxd.h"
|
||||||
|
#include "dvb-pll.h"
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
@ -313,6 +315,235 @@ static int demod_attach_lg330x(struct ngene_channel *chan)
|
||||||
return (chan->fe) ? 0 : -ENODEV;
|
return (chan->fe) ? 0 : -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int demod_attach_drxd(struct ngene_channel *chan)
|
||||||
|
{
|
||||||
|
struct drxd_config *feconf;
|
||||||
|
|
||||||
|
feconf = chan->dev->card_info->fe_config[chan->number];
|
||||||
|
|
||||||
|
chan->fe = dvb_attach(drxd_attach, feconf, chan,
|
||||||
|
&chan->i2c_adapter, &chan->dev->pci_dev->dev);
|
||||||
|
if (!chan->fe) {
|
||||||
|
pr_err("No DRXD found!\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address,
|
||||||
|
&chan->i2c_adapter,
|
||||||
|
feconf->pll_type)) {
|
||||||
|
pr_err("No pll(%d) found!\n", feconf->pll_type);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************/
|
||||||
|
/* EEPROM TAGS **************************************************************/
|
||||||
|
/****************************************************************************/
|
||||||
|
|
||||||
|
#define MICNG_EE_START 0x0100
|
||||||
|
#define MICNG_EE_END 0x0FF0
|
||||||
|
|
||||||
|
#define MICNG_EETAG_END0 0x0000
|
||||||
|
#define MICNG_EETAG_END1 0xFFFF
|
||||||
|
|
||||||
|
/* 0x0001 - 0x000F reserved for housekeeping */
|
||||||
|
/* 0xFFFF - 0xFFFE reserved for housekeeping */
|
||||||
|
|
||||||
|
/* Micronas assigned tags
|
||||||
|
EEProm tags for hardware support */
|
||||||
|
|
||||||
|
#define MICNG_EETAG_DRXD1_OSCDEVIATION 0x1000 /* 2 Bytes data */
|
||||||
|
#define MICNG_EETAG_DRXD2_OSCDEVIATION 0x1001 /* 2 Bytes data */
|
||||||
|
|
||||||
|
#define MICNG_EETAG_MT2060_1_1STIF 0x1100 /* 2 Bytes data */
|
||||||
|
#define MICNG_EETAG_MT2060_2_1STIF 0x1101 /* 2 Bytes data */
|
||||||
|
|
||||||
|
/* Tag range for OEMs */
|
||||||
|
|
||||||
|
#define MICNG_EETAG_OEM_FIRST 0xC000
|
||||||
|
#define MICNG_EETAG_OEM_LAST 0xFFEF
|
||||||
|
|
||||||
|
static int i2c_write_eeprom(struct i2c_adapter *adapter,
|
||||||
|
u8 adr, u16 reg, u8 data)
|
||||||
|
{
|
||||||
|
u8 m[3] = {(reg >> 8), (reg & 0xff), data};
|
||||||
|
struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m,
|
||||||
|
.len = sizeof(m)};
|
||||||
|
|
||||||
|
if (i2c_transfer(adapter, &msg, 1) != 1) {
|
||||||
|
pr_err(DEVICE_NAME ": Error writing EEPROM!\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_read_eeprom(struct i2c_adapter *adapter,
|
||||||
|
u8 adr, u16 reg, u8 *data, int len)
|
||||||
|
{
|
||||||
|
u8 msg[2] = {(reg >> 8), (reg & 0xff)};
|
||||||
|
struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
|
||||||
|
.buf = msg, .len = 2 },
|
||||||
|
{.addr = adr, .flags = I2C_M_RD,
|
||||||
|
.buf = data, .len = len} };
|
||||||
|
|
||||||
|
if (i2c_transfer(adapter, msgs, 2) != 2) {
|
||||||
|
pr_err(DEVICE_NAME ": Error reading EEPROM\n");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReadEEProm(struct i2c_adapter *adapter,
|
||||||
|
u16 Tag, u32 MaxLen, u8 *data, u32 *pLength)
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
u16 Addr = MICNG_EE_START, Length, tag = 0;
|
||||||
|
u8 EETag[3];
|
||||||
|
|
||||||
|
while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
|
||||||
|
if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
|
||||||
|
return -1;
|
||||||
|
tag = (EETag[0] << 8) | EETag[1];
|
||||||
|
if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
|
||||||
|
return -1;
|
||||||
|
if (tag == Tag)
|
||||||
|
break;
|
||||||
|
Addr += sizeof(u16) + 1 + EETag[2];
|
||||||
|
}
|
||||||
|
if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
|
||||||
|
pr_err(DEVICE_NAME
|
||||||
|
": Reached EOEE @ Tag = %04x Length = %3d\n",
|
||||||
|
tag, EETag[2]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Length = EETag[2];
|
||||||
|
if (Length > MaxLen)
|
||||||
|
Length = (u16) MaxLen;
|
||||||
|
if (Length > 0) {
|
||||||
|
Addr += sizeof(u16) + 1;
|
||||||
|
status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length);
|
||||||
|
if (!status) {
|
||||||
|
*pLength = EETag[2];
|
||||||
|
if (Length < EETag[2])
|
||||||
|
; /*status=STATUS_BUFFER_OVERFLOW; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int WriteEEProm(struct i2c_adapter *adapter,
|
||||||
|
u16 Tag, u32 Length, u8 *data)
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
u16 Addr = MICNG_EE_START;
|
||||||
|
u8 EETag[3];
|
||||||
|
u16 tag = 0;
|
||||||
|
int retry, i;
|
||||||
|
|
||||||
|
while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
|
||||||
|
if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
|
||||||
|
return -1;
|
||||||
|
tag = (EETag[0] << 8) | EETag[1];
|
||||||
|
if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
|
||||||
|
return -1;
|
||||||
|
if (tag == Tag)
|
||||||
|
break;
|
||||||
|
Addr += sizeof(u16) + 1 + EETag[2];
|
||||||
|
}
|
||||||
|
if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
|
||||||
|
pr_err(DEVICE_NAME
|
||||||
|
": Reached EOEE @ Tag = %04x Length = %3d\n",
|
||||||
|
tag, EETag[2]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Length > EETag[2])
|
||||||
|
return -EINVAL;
|
||||||
|
/* Note: We write the data one byte at a time to avoid
|
||||||
|
issues with page sizes. (which are different for
|
||||||
|
each manufacture and eeprom size)
|
||||||
|
*/
|
||||||
|
Addr += sizeof(u16) + 1;
|
||||||
|
for (i = 0; i < Length; i++, Addr++) {
|
||||||
|
status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]);
|
||||||
|
|
||||||
|
if (status)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Poll for finishing write cycle */
|
||||||
|
retry = 10;
|
||||||
|
while (retry) {
|
||||||
|
u8 Tmp;
|
||||||
|
|
||||||
|
msleep(50);
|
||||||
|
status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1);
|
||||||
|
if (status)
|
||||||
|
break;
|
||||||
|
if (Tmp != data[i])
|
||||||
|
pr_err(DEVICE_NAME
|
||||||
|
"eeprom write error\n");
|
||||||
|
retry -= 1;
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
pr_err(DEVICE_NAME
|
||||||
|
": Timeout polling eeprom\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data)
|
||||||
|
{
|
||||||
|
int stat;
|
||||||
|
u8 buf[2];
|
||||||
|
u32 len = 0;
|
||||||
|
|
||||||
|
stat = ReadEEProm(adapter, tag, 2, buf, &len);
|
||||||
|
if (stat)
|
||||||
|
return stat;
|
||||||
|
if (len != 2)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*data = (buf[0] << 8) | buf[1];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data)
|
||||||
|
{
|
||||||
|
int stat;
|
||||||
|
u8 buf[2];
|
||||||
|
|
||||||
|
buf[0] = data >> 8;
|
||||||
|
buf[1] = data & 0xff;
|
||||||
|
stat = WriteEEProm(adapter, tag, 2, buf);
|
||||||
|
if (stat)
|
||||||
|
return stat;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static s16 osc_deviation(void *priv, s16 deviation, int flag)
|
||||||
|
{
|
||||||
|
struct ngene_channel *chan = priv;
|
||||||
|
struct i2c_adapter *adap = &chan->i2c_adapter;
|
||||||
|
u16 data = 0;
|
||||||
|
|
||||||
|
if (flag) {
|
||||||
|
data = (u16) deviation;
|
||||||
|
pr_info(DEVICE_NAME ": write deviation %d\n",
|
||||||
|
deviation);
|
||||||
|
eeprom_write_ushort(adap, 0x1000 + chan->number, data);
|
||||||
|
} else {
|
||||||
|
if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data))
|
||||||
|
data = 0;
|
||||||
|
pr_info(DEVICE_NAME ": read deviation %d\n",
|
||||||
|
(s16) data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (s16) data;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
/* Switch control (I2C gates, etc.) *****************************************/
|
/* Switch control (I2C gates, etc.) *****************************************/
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
@ -464,6 +695,37 @@ static struct ngene_info ngene_info_m780 = {
|
||||||
.fw_version = 15,
|
.fw_version = 15,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct drxd_config fe_terratec_dvbt_0 = {
|
||||||
|
.index = 0,
|
||||||
|
.demod_address = 0x70,
|
||||||
|
.demod_revision = 0xa2,
|
||||||
|
.demoda_address = 0x00,
|
||||||
|
.pll_address = 0x60,
|
||||||
|
.pll_type = DVB_PLL_THOMSON_DTT7520X,
|
||||||
|
.clock = 20000,
|
||||||
|
.osc_deviation = osc_deviation,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct drxd_config fe_terratec_dvbt_1 = {
|
||||||
|
.index = 1,
|
||||||
|
.demod_address = 0x71,
|
||||||
|
.demod_revision = 0xa2,
|
||||||
|
.demoda_address = 0x00,
|
||||||
|
.pll_address = 0x60,
|
||||||
|
.pll_type = DVB_PLL_THOMSON_DTT7520X,
|
||||||
|
.clock = 20000,
|
||||||
|
.osc_deviation = osc_deviation,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ngene_info ngene_info_terratec = {
|
||||||
|
.type = NGENE_TERRATEC,
|
||||||
|
.name = "Terratec Integra/Cinergy2400i Dual DVB-T",
|
||||||
|
.io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
|
||||||
|
.demod_attach = {demod_attach_drxd, demod_attach_drxd},
|
||||||
|
.fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1},
|
||||||
|
.i2c_access = 1,
|
||||||
|
};
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
@ -488,6 +750,7 @@ static const struct pci_device_id ngene_id_tbl[] __devinitdata = {
|
||||||
NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex),
|
NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex),
|
||||||
NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
|
NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
|
||||||
NGENE_ID(0x1461, 0x062e, ngene_info_m780),
|
NGENE_ID(0x1461, 0x062e, ngene_info_m780),
|
||||||
|
NGENE_ID(0x153b, 0x1167, ngene_info_terratec),
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pci, ngene_id_tbl);
|
MODULE_DEVICE_TABLE(pci, ngene_id_tbl);
|
||||||
|
|
Loading…
Reference in New Issue