From: Bud <knightrider@xxxxxx> DVB driver for Earthsoft PT3 (PCIE ISDB-S/T receiver) ----------------------------------------------------- Status: stable Features: - tuning enhancements, from PT1 DVB 1. in addition to the real frequency: ISDB-S : freq. channel ID ISDB-T : freq# (I/O# +128), ch#, ch# +64 for CATV 2. in addition to TSID: ISDB-S : slot# - allocated devices ISDB-S : /dev/dvb/adapter0, /dev/dvb/adapter1 ISDB-T : /dev/dvb/adapter2, /dev/dvb/adapter3 Main components: 1. Sharp VA4M6JC2103 : contains 2 ISDB-S + 2 ISDB-T tuners ISDB-S : Sharp QM1D1C0042 RF-IC ISDB-T : MaxLinear CMOS Hybrid TV MxL301RF 2. Toshiba TC90522XBG : quad demodulator (2ch OFDM + 2ch 8PSK) 3. Altera EP4CGX15BF14C8N : customized FPGA PCI bridge Full package: - URL: https://github.com/knight-rider/ptx/tree/master/pt3_dvb - buildable as standalone, DKMS or tree embedded module - installation: $ chmod +x dkms.install dkms.uninstall $ ./dkms.install Changes since last release: - cleanups (removed unused/useless features, simple is the best) - removed .ops.write hacks Signed-off-by: Буди Романто, AreMa Inc <knightrider@xxxxxx> --- drivers/media/dvb-frontends/Kconfig | 18 +- drivers/media/dvb-frontends/Makefile | 3 +- drivers/media/dvb-frontends/tc90522.c | 525 ++++++++++++++++++++++++++++++++++ drivers/media/dvb-frontends/tc90522.h | 33 +++ diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 704403f..292694e 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -621,7 +621,7 @@ config DVB_S5H1411 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. -comment "ISDB-T (terrestrial) frontends" +comment "ISDB-S (satellite) & ISDB-T (terrestrial) frontends" depends on DVB_CORE config DVB_S921 @@ -648,6 +648,15 @@ config DVB_MB86A20S A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator. Say Y when you want to support this frontend. +config DVB_TC90522 + tristate "Toshiba TC90522XBG OFDM(ISDB-T)/8PSK(ISDB-S)" + depends on DVB_CORE && I2C + default m if !MEDIA_SUBDRV_AUTOSELECT + help + Toshiba TC90522XBG OFDM(ISDB-T)/8PSK(ISDB-S) demodulator + frontend for Earthsoft PT3 PCIE cards. + Say Y when you want to support this frontend. + comment "Digital terrestrial only tuners/PLL" depends on DVB_CORE @@ -725,13 +734,6 @@ config DVB_A8293 depends on DVB_CORE && I2C default m if !MEDIA_SUBDRV_AUTOSELECT -config DVB_SP2 - tristate "CIMaX SP2" - depends on DVB_CORE && I2C - default m if !MEDIA_SUBDRV_AUTOSELECT - help - CIMaX SP2/SP2HF Common Interface module. - config DVB_LGS8GL5 tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" depends on DVB_CORE && I2C diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 1e19a74..8f9a229 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -107,7 +107,6 @@ obj-$(CONFIG_DVB_DRXK) += drxk.o obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o obj-$(CONFIG_DVB_SI2165) += si2165.o obj-$(CONFIG_DVB_A8293) += a8293.o -obj-$(CONFIG_DVB_SP2) += sp2.o obj-$(CONFIG_DVB_TDA10071) += tda10071.o obj-$(CONFIG_DVB_RTL2830) += rtl2830.o obj-$(CONFIG_DVB_RTL2832) += rtl2832.o @@ -115,3 +114,5 @@ obj-$(CONFIG_DVB_RTL2832_SDR) += rtl2832_sdr.o obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o obj-$(CONFIG_DVB_AF9033) += af9033.o obj-$(CONFIG_DVB_AS102_FE) += as102_fe.o +obj-$(CONFIG_DVB_TC90522) += tc90522.o + diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c new file mode 100644 index 0000000..26e2efe --- /dev/null +++ b/drivers/media/dvb-frontends/tc90522.c @@ -0,0 +1,525 @@ +/* + * Toshiba TC90522XBG 2ch OFDM(ISDB-T) + 2ch 8PSK(ISDB-S) demodulator frontend for Earthsoft PT3 + * + * Copyright (C) 2014 Budi Rachmanto, AreMa Inc. <info@xxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "dvb_math.h" +#include "tc90522.h" + +MODULE_AUTHOR("Budi Rachmanto, AreMa Inc. <knightrider(@)are.ma>"); +MODULE_DESCRIPTION("Toshiba TC90522 OFDM(ISDB-T)/8PSK(ISDB-S) demodulator [Earthsoft PT3]"); +MODULE_LICENSE("GPL"); + +#define TC90522_PASSTHROUGH 0xfe + +enum tc90522_state { + TC90522_IDLE, + TC90522_SET_FREQUENCY, + TC90522_SET_MODULATION, + TC90522_TRACK, + TC90522_ABORT, +}; + +struct tc90522 { + struct dvb_frontend fe; + struct i2c_adapter *i2c; + fe_delivery_system_t type; + u8 idx, addr_demod; + s32 offset; + enum tc90522_state state; +}; + +int tc90522_write(struct dvb_frontend *fe, const u8 *data, int len) +{ + struct tc90522 *demod = fe->demodulator_priv; + struct i2c_msg msg[] = { + { .addr = demod->addr_demod, .flags = 0, .buf = (u8 *)data, .len = len, }, + }; + + if (!data || !len) + return -EINVAL; + return i2c_transfer(demod->i2c, msg, 1) == 1 ? 0 : -EREMOTEIO; +} + +int tc90522_write_data(struct dvb_frontend *fe, u8 addr_data, u8 *data, u8 len) +{ + u8 buf[len + 1]; + + buf[0] = addr_data; + memcpy(buf + 1, data, len); + return tc90522_write(fe, buf, len + 1); +} + +int tc90522_read(struct tc90522 *demod, u8 addr, u8 *buf, u8 len) +{ + struct i2c_msg msg[] = { + { .addr = demod->addr_demod, .flags = 0, .buf = buf, .len = 1, }, + { .addr = demod->addr_demod, .flags = I2C_M_RD, .buf = buf, .len = len, }, + }; + if (!buf || !len) + return -EINVAL; + buf[0] = addr; + return i2c_transfer(demod->i2c, msg, 2) == 2 ? 0 : -EREMOTEIO; +} + +u64 tc90522_n2int(const u8 *data, u8 n) /* convert n_bytes data from stream (network byte order) to integer */ +{ /* can't use <arpa/inet.h>'s ntoh*() as sometimes n = 3,5,... */ + u32 i, val = 0; + + for (i = 0; i < n; i++) { + val <<= 8; + val |= data[i]; + } + return val; +} + +int tc90522_read_id_s(struct tc90522 *demod, u16 *id) +{ + u8 buf[2]; + int err = tc90522_read(demod, 0xe6, buf, 2); + + if (!err) + *id = tc90522_n2int(buf, 2); + return err; +} + +struct tmcc_s { /* Transmission and Multiplexing Configuration Control */ + u32 mode[4]; + u32 slot[4]; + u32 id[8]; +}; + +int tc90522_read_tmcc_s(struct tc90522 *demod, struct tmcc_s *tmcc) +{ + enum { + BASE = 0xc5, + SIZE = 0xe5 - BASE + 1 + }; + u8 data[SIZE]; + u32 i, byte_offset, bit_offset; + + int err = tc90522_read(demod, 0xc3, data, 1) || + ((data[0] >> 4) & 1) || + tc90522_read(demod, 0xce, data, 2) || + (tc90522_n2int(data, 2) == 0) || + tc90522_read(demod, 0xc3, data, 1) || + tc90522_read(demod, 0xc5, data, SIZE); + if (err) + return err; + for (i = 0; i < 4; i++) { + byte_offset = i >> 1; + bit_offset = (i & 1) ? 0 : 4; + tmcc->mode[i] = (data[0xc8 + byte_offset - BASE] >> bit_offset) & 0b00001111; + tmcc->slot[i] = (data[0xca + i - BASE] >> 0) & 0b00111111; + } + for (i = 0; i < 8; i++) + tmcc->id[i] = tc90522_n2int(data + 0xce + i * 2 - BASE, 2); + return 0; +} + +enum tc90522_pwr { + TC90522_PWR_OFF = 0x00, + TC90522_PWR_AMP_ON = 0x04, + TC90522_PWR_TUNER_ON = 0x40, +}; + +int tc90522_set_powers(struct tc90522 *demod, enum tc90522_pwr pwr) +{ + u8 data = pwr | 0b10011001; + + dev_dbg(&demod->i2c->dev, "%s #%d %s tuner %s amp %s\n", demod->i2c->name, demod->idx, __func__, pwr & TC90522_PWR_TUNER_ON ? + "ON" : "OFF", pwr & TC90522_PWR_AMP_ON ? "ON" : "OFF"); + return tc90522_write_data(&demod->fe, 0x1e, &data, 1); +} + +/* dvb_frontend_ops */ +int tc90522_get_frontend_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +int tc90522_sleep(struct dvb_frontend *fe) +{ + struct tc90522 *demod = fe->demodulator_priv; + + dev_dbg(&demod->i2c->dev, "%s #%d %s %s\n", demod->i2c->name, demod->idx, __func__, demod->type == SYS_ISDBS ? "S" : "T"); + return fe->ops.tuner_ops.sleep(fe); +} + +int tc90522_wakeup(struct dvb_frontend *fe) +{ + struct tc90522 *demod = fe->demodulator_priv; + + dev_dbg(&demod->i2c->dev, "%s #%d %s %s\n", demod->i2c->name, demod->idx, __func__, demod->type == SYS_ISDBS ? "S" : "T"); + demod->state = TC90522_IDLE; + return fe->ops.tuner_ops.init(fe); +} + +void tc90522_release(struct dvb_frontend *fe) +{ + struct tc90522 *demod = fe->demodulator_priv; + + dev_dbg(&demod->i2c->dev, "%s #%d %s\n", demod->i2c->name, demod->idx, __func__); + tc90522_set_powers(demod, TC90522_PWR_OFF); + tc90522_sleep(fe); + fe->ops.tuner_ops.release(fe); + kfree(demod); +} + +s64 tc90522_get_cn_raw(struct tc90522 *demod) +{ + u8 buf[3], buflen = demod->type == SYS_ISDBS ? 2 : 3, addr = demod->type == SYS_ISDBS ? 0xbc : 0x8b; + int err = tc90522_read(demod, addr, buf, buflen); + + return err < 0 ? err : tc90522_n2int(buf, buflen); +} + +s64 tc90522_get_cn_s(s64 raw) /* @ .0001 dB */ +{ + s64 x, y; + + raw -= 3000; + if (raw < 0) + raw = 0; + x = int_sqrt(raw << 20); + y = 16346ll * x - (143410ll << 16); + y = ((x * y) >> 16) + (502590ll << 16); + y = ((x * y) >> 16) - (889770ll << 16); + y = ((x * y) >> 16) + (895650ll << 16); + y = (588570ll << 16) - ((x * y) >> 16); + return y < 0 ? 0 : y >> 16; +} + +s64 tc90522_get_cn_t(s64 raw) /* @ .0001 dB */ +{ + s64 x, y; + + if (!raw) + return 0; + x = (1130911733ll - 10ll * intlog10(raw)) >> 2; + y = (6ll * x / 25ll) - (16ll << 22); + y = ((x * y) >> 22) + (398ll << 22); + y = ((x * y) >> 22) + (5491ll << 22); + y = ((x * y) >> 22) + (30965ll << 22); + return y >> 22; +} + +int tc90522_read_snr(struct dvb_frontend *fe, u16 *cn) /* raw C/N, digitally modulated S/N ratio */ +{ + struct tc90522 *demod = fe->demodulator_priv; + s64 err = tc90522_get_cn_raw(demod); + *cn = err < 0 ? 0 : err; + dev_dbg(&demod->i2c->dev, "%s v3 CN %d (%lld dB)\n", demod->i2c->name, (int)*cn, + demod->type == SYS_ISDBS ? (int64_t)tc90522_get_cn_s(*cn) : (int64_t)tc90522_get_cn_t(*cn)); + return err < 0 ? err : 0; +} + +int tc90522_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct tc90522 *demod = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + s64 err = tc90522_get_cn_raw(demod), + raw = err < 0 ? 0 : err; + + switch (demod->state) { + case TC90522_IDLE: + case TC90522_SET_FREQUENCY: + *status = 0; + break; + + case TC90522_SET_MODULATION: + case TC90522_ABORT: + *status |= FE_HAS_SIGNAL; + break; + + case TC90522_TRACK: + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + break; + } + + c->cnr.len = 1; + c->cnr.stat[0].svalue = demod->type == SYS_ISDBS ? tc90522_get_cn_s(raw) : tc90522_get_cn_t(raw); + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + dev_dbg(&demod->i2c->dev, "%s v5 CN %lld (%lld dB)\n", demod->i2c->name, raw, c->cnr.stat[0].svalue); + return err < 0 ? err : 0; +} + +/**** ISDB-S ****/ +int tc90522_tune_s(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, fe_status_t *status) +{ + struct tc90522 *demod = fe->demodulator_priv; + struct tmcc_s tmcc; + int i, err, + freq = fe->dtv_property_cache.frequency, + tsid = fe->dtv_property_cache.stream_id; + u8 id_s[2]; + + if (re_tune) + demod->state = TC90522_SET_FREQUENCY; + + switch (demod->state) { + case TC90522_IDLE: + *delay = msecs_to_jiffies(2000); + *status = 0; + return 0; + + case TC90522_SET_FREQUENCY: + dev_dbg(&demod->i2c->dev, "%s #%d tsid 0x%x freq %d\n", demod->i2c->name, demod->idx, tsid, freq); + err = fe->ops.tuner_ops.set_frequency(fe, freq); + if (err) + return err; + demod->offset = 0; + demod->state = TC90522_SET_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case TC90522_SET_MODULATION: + for (i = 0; i < 1000; i++) { + err = tc90522_read_tmcc_s(demod, &tmcc); + if (!err) + break; + msleep_interruptible(1); + } + if (err) { + dev_dbg(&demod->i2c->dev, "%s fail tc_read_tmcc_s err=0x%x\n", demod->i2c->name, err); + demod->state = TC90522_ABORT; + *delay = msecs_to_jiffies(1000); + return err; + } + dev_dbg(&demod->i2c->dev, "%s slots=%d,%d,%d,%d mode=%d,%d,%d,%d tmcc.id=0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n", + demod->i2c->name, + tmcc.slot[0], tmcc.slot[1], tmcc.slot[2], tmcc.slot[3], + tmcc.mode[0], tmcc.mode[1], tmcc.mode[2], tmcc.mode[3], + tmcc.id[0], tmcc.id[1], tmcc.id[2], tmcc.id[3], + tmcc.id[4], tmcc.id[5], tmcc.id[6], tmcc.id[7]); + for (i = 0; i < ARRAY_SIZE(tmcc.id); i++) { + dev_dbg(&demod->i2c->dev, "%s tsid %x i %d tmcc.id %x\n", demod->i2c->name, tsid, i, tmcc.id[i]); + if (tmcc.id[i] == tsid) + break; + } + if (tsid < ARRAY_SIZE(tmcc.id)) /* treat as slot# */ + i = tsid; + if (i == ARRAY_SIZE(tmcc.id)) { + dev_dbg(&demod->i2c->dev, "%s #%d i%d tsid 0x%x not found\n", demod->i2c->name, demod->idx, i, tsid); + return -EINVAL; + } + demod->offset = i; + dev_dbg(&demod->i2c->dev, "%s #%d found tsid 0x%x on slot %d\n", demod->i2c->name, demod->idx, tsid, i); + + id_s[0] = (tmcc.id[demod->offset] >> 8) & 0xff; + id_s[1] = tmcc.id[demod->offset] & 0xff; + err = tc90522_write_data(fe, 0x8f, id_s, sizeof(id_s)); + if (err) { + dev_dbg(&demod->i2c->dev, "%s fail set_tmcc_s err=%d\n", demod->i2c->name, err); + return err; + } + for (i = 0; i < 1000; i++) { + u16 short_id; + + err = tc90522_read_id_s(demod, &short_id); + if (err) { + dev_dbg(&demod->i2c->dev, "%s fail get_id_s err=%d\n", demod->i2c->name, err); + return err; + } + tsid = short_id; + dev_dbg(&demod->i2c->dev, "%s #%d tsid=0x%x\n", demod->i2c->name, demod->idx, tsid); + if ((tsid & 0xffff) == tmcc.id[demod->offset]) + break; + msleep_interruptible(1); + } + demod->state = TC90522_TRACK; + /* fallthrough */ + + case TC90522_TRACK: + *delay = msecs_to_jiffies(2000); + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + + case TC90522_ABORT: + *delay = msecs_to_jiffies(2000); + *status = FE_HAS_SIGNAL; + return 0; + } + return -ERANGE; +} + +int tc90522_read_tuner_s(struct dvb_frontend *fe, u8 *addr, int len) +{ + struct tc90522 *demod = fe->demodulator_priv; + u8 buf[] = { TC90522_PASSTHROUGH, addr[0] << 1, addr[1], TC90522_PASSTHROUGH, (addr[0] << 1) | 1, 0 }; + struct i2c_msg msg[] = { + { .addr = demod->addr_demod, .flags = 0, .buf = buf, .len = 3, }, + { .addr = demod->addr_demod, .flags = 0, .buf = buf + 3, .len = 2, }, + { .addr = demod->addr_demod, .flags = I2C_M_RD, .buf = buf + 5, .len = 1, }, + }; + + if (!addr || (len != 2)) + return -EINVAL; + return i2c_transfer(demod->i2c, msg, 3) == 3 ? buf[5] : -EREMOTEIO; +} + +static struct dvb_frontend_ops tc90522_ops_s = { + .delsys = { SYS_ISDBS }, + .info = { + .name = "TC90522 ISDB-S", + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_MULTISTREAM | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + .init = tc90522_wakeup, + .sleep = tc90522_sleep, + .release = tc90522_release, + .write = tc90522_write, + .get_frontend_algo = tc90522_get_frontend_algo, + .read_snr = tc90522_read_snr, + .read_status = tc90522_read_status, + .tune = tc90522_tune_s, + .tuner_ops.calc_regs = tc90522_read_tuner_s, +}; + +/**** ISDB-T ****/ +int tc90522_get_tmcc_t(struct tc90522 *demod) +{ + u8 buf; + u16 i = 65535; + bool b = false, retryov, fulock; + + while (i--) { + if (tc90522_read(demod, 0x80, &buf, 1)) + return -EBADMSG; + retryov = buf & 0b10000000 ? true : false; + fulock = buf & 0b00001000 ? true : false; + if (!fulock) { + b = true; + break; + } + if (retryov) + break; + msleep_interruptible(1); + } + return b ? 0 : -EBADMSG; +} + +int tc90522_tune_t(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, unsigned int *delay, fe_status_t *status) +{ + struct tc90522 *demod = fe->demodulator_priv; + int err, i; + + if (re_tune) + demod->state = TC90522_SET_FREQUENCY; + + switch (demod->state) { + case TC90522_IDLE: + *delay = msecs_to_jiffies(2000); + *status = 0; + return 0; + + case TC90522_SET_FREQUENCY: + if (fe->ops.tuner_ops.set_frequency(fe, fe->dtv_property_cache.frequency)) { + *delay = msecs_to_jiffies(1000); + *status = 0; + return 0; + } + demod->state = TC90522_SET_MODULATION; + *delay = 0; + *status = FE_HAS_SIGNAL; + return 0; + + case TC90522_SET_MODULATION: + for (i = 0; i < 1000; i++) { + err = tc90522_get_tmcc_t(demod); + if (!err) + break; + msleep_interruptible(2); + } + if (err) { + dev_dbg(&demod->i2c->dev, "%s #%d fail get_tmcc_t err=%d\n", demod->i2c->name, demod->idx, err); + demod->state = TC90522_ABORT; + *delay = msecs_to_jiffies(1000); + return 0; + } + demod->state = TC90522_TRACK; + /* fallthrough */ + + case TC90522_TRACK: + *delay = msecs_to_jiffies(2000); + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; + return 0; + + case TC90522_ABORT: + *delay = msecs_to_jiffies(2000); + *status = FE_HAS_SIGNAL; + return 0; + } + return -ERANGE; +} + +int tc90522_read_tuner_t(struct dvb_frontend *fe, u8 *addr, int len) +{ + struct tc90522 *demod = fe->demodulator_priv; + u8 buf[] = { TC90522_PASSTHROUGH, (addr[0] << 1) | 1, 0 }; + struct i2c_msg msg[] = { + { .addr = demod->addr_demod, .flags = 0, .buf = buf, .len = 2, }, + { .addr = demod->addr_demod, .flags = I2C_M_RD, .buf = buf + 2, .len = 1, }, + }; + + if (!addr || (len != 1)) + return -EINVAL; + return i2c_transfer(demod->i2c, msg, 2) == 2 ? buf[2] : -EREMOTEIO; +} + +static struct dvb_frontend_ops tc90522_ops_t = { + .delsys = { SYS_ISDBT }, + .info = { + .name = "TC90522 ISDB-T", + .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, + }, + .init = tc90522_wakeup, + .sleep = tc90522_sleep, + .release = tc90522_release, + .write = tc90522_write, + .get_frontend_algo = tc90522_get_frontend_algo, + .read_snr = tc90522_read_snr, + .read_status = tc90522_read_status, + .tune = tc90522_tune_t, + .tuner_ops.calc_regs = tc90522_read_tuner_t, +}; + +/**** Common ****/ +struct dvb_frontend *tc90522_attach(struct i2c_adapter *i2c, fe_delivery_system_t type, u8 addr_demod, bool pwr_on) +{ + struct dvb_frontend *fe; + struct tc90522 *demod = kzalloc(sizeof(struct tc90522), GFP_KERNEL); + + if (!demod) + return NULL; + demod->addr_demod = addr_demod; + demod->idx = (!(addr_demod & 1) << 1) + ((addr_demod & 2) >> 1); + demod->i2c = i2c; + demod->type = type; + fe = &demod->fe; + memcpy(&fe->ops, (demod->type == SYS_ISDBS) ? &tc90522_ops_s : &tc90522_ops_t, sizeof(struct dvb_frontend_ops)); + fe->demodulator_priv = demod; + + if (pwr_on && (tc90522_set_powers(demod, TC90522_PWR_TUNER_ON) || + i2c_transfer(demod->i2c, NULL, 0) || + tc90522_set_powers(demod, TC90522_PWR_TUNER_ON | TC90522_PWR_AMP_ON))) { + tc90522_release(fe); + return NULL; + } + return fe; +} +EXPORT_SYMBOL(tc90522_attach); + diff --git a/drivers/media/dvb-frontends/tc90522.h b/drivers/media/dvb-frontends/tc90522.h new file mode 100644 index 0000000..ab4911b --- /dev/null +++ b/drivers/media/dvb-frontends/tc90522.h @@ -0,0 +1,33 @@ +/* + * Earthsoft PT3 demodulator frontend Toshiba TC90522XBG OFDM(ISDB-T)/8PSK(ISDB-S) + * + * Copyright (C) 2014 Budi Rachmanto, AreMa Inc. <info@xxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __TC90522_H__ +#define __TC90522_H__ + +#include "dvb_frontend.h" + +#if IS_ENABLED(CONFIG_DVB_TC90522) +extern struct dvb_frontend *tc90522_attach(struct i2c_adapter *i2c, fe_delivery_system_t type, u8 addr_demod, bool pwr); +#else +static inline struct dvb_frontend *tc90522_attach(struct i2c_adapter *i2c, fe_delivery_system_t type, u8 addr_demod, bool pwr) +{ + dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html