diff --git a/ddbridge/Kbuild b/ddbridge/Kbuild index 9f2da9a..e43b288 100644 --- a/ddbridge/Kbuild +++ b/ddbridge/Kbuild @@ -1,7 +1,7 @@ EXTRA_CFLAGS += -DCONFIG_DVB_CXD2843 -DCONFIG_DVB_LNBP21 -DCONFIG_DVB_STV090x -DCONFIG_DVB_STV6110x -DCONFIG_DVB_DRXK -DCONFIG_DVB_STV0910 -DCONFIG_DVB_STV6111 -DCONFIG_DVB_LNBH25 -DCONFIG_DVB_MXL5XX -DCONFIG_DVB_CXD2099 -DCONFIG_DVB_NET -ddbridge-objs = ddbridge-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-io.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o -octonet-objs = octonet-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-io.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o +ddbridge-objs = ddbridge-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-io.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o ddbridge-sx8.o ddbridge-m4.o +octonet-objs = octonet-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-io.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o ddbridge-sx8.o ddbridge-m4.o obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o obj-$(CONFIG_DVB_OCTONET) += octonet.o diff --git a/ddbridge/Makefile b/ddbridge/Makefile index 42120ee..4bff221 100644 --- a/ddbridge/Makefile +++ b/ddbridge/Makefile @@ -2,8 +2,8 @@ # Makefile for the ddbridge device driver # -ddbridge-objs = ddbridge-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o -octonet-objs = octonet-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o +ddbridge-objs = ddbridge-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o ddbridge-sx8.o ddbridge-m4.o +octonet-objs = octonet-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o ddbridge-sx8.o ddbridge-m4.o obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o obj-$(CONFIG_DVB_OCTONET) += octonet.o diff --git a/ddbridge/Makefile.kernel b/ddbridge/Makefile.kernel index 401f6d9..96b00b5 100644 --- a/ddbridge/Makefile.kernel +++ b/ddbridge/Makefile.kernel @@ -2,8 +2,8 @@ # Makefile for the ddbridge device driver # -ddbridge-objs = ddbridge-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-io.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o -octonet-objs = octonet-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-io.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o +ddbridge-objs = ddbridge-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-io.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o ddbridge-sx8.o ddbridge-m4.o +octonet-objs = octonet-main.o ddbridge-hw.o ddbridge-i2c.o ddbridge-ns.o ddbridge-modulator.o ddbridge-core.o ddbridge-io.o ddbridge-ci.o ddbridge-max.o ddbridge-mci.o ddbridge-sx8.o ddbridge-m4.o obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o obj-$(CONFIG_DVB_OCTONET) += octonet.o diff --git a/ddbridge/ddbridge-core.c b/ddbridge/ddbridge-core.c index 82dcd74..953930f 100644 --- a/ddbridge/ddbridge-core.c +++ b/ddbridge/ddbridge-core.c @@ -26,7 +26,6 @@ #include "ddbridge-i2c.h" #include "ddbridge-io.h" #include "dvb_net.h" -#include "ddbridge-mci.h" struct workqueue_struct *ddb_wq; @@ -1766,8 +1765,9 @@ static int dvb_input_attach(struct ddb_input *input) if (demod_attach_dummy(input) < 0) return -ENODEV; break; - case DDB_TUNER_MCI: - if (ddb_fe_attach_mci(input) < 0) + case DDB_TUNER_MCI_SX8: + case DDB_TUNER_MCI_M4: + if (ddb_fe_attach_mci(input, port->type) < 0) return -ENODEV; break; default: @@ -2062,13 +2062,13 @@ static void ddb_port_probe(struct ddb_port *port) return; } - if (dev->link[l].info->type == DDB_OCTOPUS_MCI) { - if (port->nr >= dev->link[l].info->mci) + if (link->info->type == DDB_OCTOPUS_MCI) { + if (port->nr >= link->info->mci_ports) return; port->name = "DUAL MCI"; port->type_name = "MCI"; port->class = DDB_PORT_TUNER; - port->type = DDB_TUNER_MCI; + port->type = DDB_TUNER_MCI + link->info->mci_type; return; } diff --git a/ddbridge/ddbridge-hw.c b/ddbridge/ddbridge-hw.c index f03b818..4d12da4 100644 --- a/ddbridge/ddbridge-hw.c +++ b/ddbridge/ddbridge-hw.c @@ -582,7 +582,8 @@ static const struct ddb_info ddb_s2x_48 = { .port_num = 4, .i2c_mask = 0x00, .tempmon_irq = 24, - .mci = 4, + .mci_ports = 4, + .mci_type = 0, }; static const struct ddb_info ddb_m4 = { @@ -592,7 +593,8 @@ static const struct ddb_info ddb_m4 = { .port_num = 2, .i2c_mask = 0x00, .tempmon_irq = 24, - .mci = 2, + .mci_ports = 2, + .mci_type = 1, }; /****************************************************************************/ diff --git a/ddbridge/ddbridge-m4.c b/ddbridge/ddbridge-m4.c new file mode 100644 index 0000000..75b1d7e --- /dev/null +++ b/ddbridge/ddbridge-m4.c @@ -0,0 +1,91 @@ +/* + * ddbridge-m4.c: Digital Devices MAX M4 driver + * + * Copyright (C) 2018 Digital Devices GmbH + * Marcus Metzler + * Ralph Metzler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * 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. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, point your browser to + * http://www.gnu.org/copyleft/gpl.html + */ + +#include "ddbridge.h" +#include "ddbridge-io.h" +#include "ddbridge-i2c.h" +#include "ddbridge-mci.h" + +struct m4_base { + struct mci_base mci_base; + +}; + +struct m4 { + struct mci mci; + +}; + +static struct dvb_frontend_ops m4_ops = { + .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBT, SYS_DVBT2, SYS_DVBC2, SYS_ISDBT, + SYS_DVBS, SYS_DVBS2, }, + .info = { + .name = "M4", + .frequency_min = 47000000, /* DVB-T: 47125000 */ + .frequency_max = 865000000, /* DVB-C: 862000000 */ + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000, + .frequency_stepsize = 0, + .frequency_tolerance = 0, + .caps = FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 | + FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 | + FE_CAN_QAM_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | FE_CAN_MUTE_TS | FE_CAN_2G_MODULATION + }, +#if 0 + .get_frontend_algo = get_algo, + .tune = tune, + .release = release, + .read_status = read_status, + .set_input = set_input, + .sleep = sleep, +#endif +}; + +static int init(struct mci *mci) +{ + //struct m4 *state = (struct m4 *) mci; + + return 0; +} + +static int base_init(struct mci_base *mci_base) +{ + //struct m4_base *base = (struct m4_base *) mci_base; + + return 0; +} + +struct mci_cfg ddb_max_m4_cfg = { + .type = 0, + .fe_ops = &m4_ops, + .base_size = sizeof(struct m4_base), + .state_size = sizeof(struct m4), + .init = init, + .base_init = base_init, +}; diff --git a/ddbridge/ddbridge-max.c b/ddbridge/ddbridge-max.c index 6f905c1..7374e25 100644 --- a/ddbridge/ddbridge-max.c +++ b/ddbridge/ddbridge-max.c @@ -24,7 +24,6 @@ #include "ddbridge.h" #include "ddbridge-io.h" #include "ddbridge-i2c.h" -#include "ddbridge-mci.h" /* MAX LNB interface related module parameters */ @@ -445,11 +444,10 @@ int ddb_fe_attach_mxl5xx(struct ddb_input *input) /* MAX MCI related functions */ -static struct mci_cfg maxsx8 = { +extern struct mci_cfg ddb_max_sx8_cfg; +extern struct mci_cfg ddb_max_m4_cfg; -}; - -int ddb_fe_attach_mci(struct ddb_input *input) +int ddb_fe_attach_mci(struct ddb_input *input, u32 type) { struct ddb *dev = input->port->dev; //struct i2c_adapter *i2c = &input->port->i2c->adap; @@ -459,14 +457,23 @@ int ddb_fe_attach_mci(struct ddb_input *input) int demod, tuner; struct mci_cfg cfg; - cfg = maxsx8; demod = input->nr; tuner = demod & 3; - if (fmode == 3) - tuner = 0; - dvb->fe = ddb_mci_attach(input, 0, demod); + switch (type) { + case DDB_TUNER_MCI_SX8: + cfg = ddb_max_sx8_cfg; + if (fmode == 3) + tuner = 0; + break; + case DDB_TUNER_MCI_M4: + cfg = ddb_max_m4_cfg; + break; + default: + return -EINVAL; + } + dvb->fe = ddb_mci_attach(input, &cfg, demod); if (!dvb->fe) { - dev_err(dev->dev, "No MAXSX8 found!\n"); + dev_err(dev->dev, "No MCI card found!\n"); return -ENODEV; } if (input->nr < 4) { diff --git a/ddbridge/ddbridge-mci.c b/ddbridge/ddbridge-mci.c index 836375d..34109f9 100644 --- a/ddbridge/ddbridge-mci.c +++ b/ddbridge/ddbridge-mci.c @@ -27,43 +27,6 @@ static LIST_HEAD(mci_list); -static const u32 MCLK = (1550000000/12); -static const u32 MAX_LDPC_BITRATE = (720000000); - -struct mci_base { - struct list_head mci_list; - void *key; - struct ddb_link *link; - struct completion completion; - - struct i2c_adapter *i2c; - struct mutex i2c_lock; - struct mutex tuner_lock; - u8 adr; - struct mutex mci_lock; - int count; - - u8 tuner_use_count[4]; - u8 assigned_demod[8]; - u32 used_ldpc_bitrate[8]; - u8 demod_in_use[8]; - u32 iq_mode; -}; - -struct mci { - struct mci_base *base; - struct dvb_frontend fe; - int nr; - int demod; - int tuner; - int first_time_lock; - int started; - struct mci_result signal_info; - - u32 bb_mode; -}; - - static int mci_reset(struct mci *state) { struct ddb_link *link = state->base->link; @@ -90,7 +53,7 @@ static int mci_reset(struct mci *state) return 0; } -static int mci_config(struct mci *state, u32 config) +int ddb_mci_config(struct mci *state, u32 config) { struct ddb_link *link = state->base->link; @@ -101,9 +64,9 @@ static int mci_config(struct mci *state, u32 config) } -static int _mci_cmd_unlocked(struct mci *state, - u32 *cmd, u32 cmd_len, - u32 *res, u32 res_len) +static int ddb_mci_cmd_raw_unlocked(struct mci *state, + u32 *cmd, u32 cmd_len, + u32 *res, u32 res_len) { struct ddb_link *link = state->base->link; u32 i, val; @@ -129,45 +92,63 @@ static int _mci_cmd_unlocked(struct mci *state, return 0; } -static int mci_cmd_unlocked(struct mci *state, - struct mci_command *command, - struct mci_result *result) +int ddb_mci_cmd_unlocked(struct mci *state, + struct mci_command *command, + struct mci_result *result) { u32 *cmd = (u32 *) command; u32 *res = (u32 *) result; - return _mci_cmd_unlocked(state, cmd, sizeof(*command)/sizeof(u32), - res, sizeof(*result)/sizeof(u32)); + return ddb_mci_cmd_raw_unlocked(state, cmd, sizeof(*command)/sizeof(u32), + res, sizeof(*result)/sizeof(u32)); } -static int mci_cmd(struct mci *state, - struct mci_command *command, - struct mci_result *result) +int ddb_mci_cmd(struct mci *state, + struct mci_command *command, + struct mci_result *result) { int stat; - + mutex_lock(&state->base->mci_lock); - stat = _mci_cmd_unlocked(state, + stat = ddb_mci_cmd_raw_unlocked(state, (u32 *)command, sizeof(*command)/sizeof(u32), (u32 *)result, sizeof(*result)/sizeof(u32)); mutex_unlock(&state->base->mci_lock); return stat; } -static int _mci_cmd(struct mci *state, +int ddb_mci_cmd_raw(struct mci *state, struct mci_command *command, u32 command_len, struct mci_result *result, u32 result_len) { int stat; - + mutex_lock(&state->base->mci_lock); - stat = _mci_cmd_unlocked(state, - (u32 *)command, command_len, - (u32 *)result, result_len); + stat = ddb_mci_cmd_raw_unlocked(state, + (u32 *)command, command_len, + (u32 *)result, result_len); mutex_unlock(&state->base->mci_lock); return stat; } +static int ddb_mci_get_iq(struct mci *mci, u32 demod, s16 *i, s16 *q) +{ + int stat; + struct mci_command cmd; + struct mci_result res; + + memset(&cmd, 0, sizeof(cmd)); + memset(&res, 0, sizeof(res)); + cmd.command = MCI_CMD_GET_IQSYMBOL; + cmd.demod = demod; + stat = ddb_mci_cmd(mci, &cmd, &res); + if (!stat) { + *i = res.iq_symbol.i; + *q = res.iq_symbol.q; + } + return stat; +} + static void mci_handler(void *priv) { struct mci_base *base = (struct mci_base *)priv; @@ -175,422 +156,6 @@ static void mci_handler(void *priv) complete(&base->completion); } - - -static const u8 dvbs2_bits_per_symbol[] = { - 0, 0, 0, 0, - /* S2 QPSK */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - /* S2 8PSK */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - /* S2 16APSK */ - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - /* S2 32APSK */ - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, - - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 3, 0, 4, 0, - 2, 2, 2, 2, 2, 2, // S2X QPSK - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // S2X 8PSK - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // S2X 16APSK - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // S2X 32APSK - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // S2X 64APSK - 7, 7, 7, 7, // S2X 128APSK - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // S2X 256APSK - - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // S2X QPSK - 3, 3, 3, 3, 3, 3, 3, 3, // S2X 8PSK - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // S2X 16APSK - 5, 5, 5, 5, // S2X 32APSK - - 3, 4, 5, 6, 8, 10, -}; - - -static void release(struct dvb_frontend *fe) -{ - struct mci *state = fe->demodulator_priv; - - state->base->count--; - if (state->base->count == 0) { - list_del(&state->base->mci_list); - kfree(state->base); - } - kfree(state); -} - -static int get_info(struct dvb_frontend *fe) -{ - int stat; - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - - memset(&cmd, 0, sizeof(cmd)); - cmd.command = MCI_CMD_GETSIGNALINFO; - cmd.demod = state->demod; - stat = mci_cmd(state, &cmd, &state->signal_info); - return stat; -} - -static int get_snr(struct dvb_frontend *fe) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - - p->cnr.len = 1; - p->cnr.stat[0].scale = FE_SCALE_DECIBEL; - p->cnr.stat[0].svalue = (s64) state->signal_info.dvbs2_signal_info.signal_to_noise * 100; - return 0; -} - -static int get_strength(struct dvb_frontend *fe) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - s32 str; - - str = 100000 - (state->signal_info.dvbs2_signal_info.channel_power * 10 + 108750); - p->strength.len = 1; - p->strength.stat[0].scale = FE_SCALE_DECIBEL; - p->strength.stat[0].svalue = str; - return 0; -} - -static int read_status(struct dvb_frontend *fe, enum fe_status *status) -{ - int stat; - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - u32 val; - struct mci_result *res = (struct mci_result *)&val; - - cmd.command = MCI_CMD_GETSTATUS; - cmd.demod = state->demod; - stat = _mci_cmd(state, &cmd, 1, res, 1); - if (stat) - return stat; - *status = 0x00; - get_info(fe); - get_strength(fe); - if (res->status == SX8_DEMOD_WAIT_MATYPE) - *status = 0x0f; - if (res->status == SX8_DEMOD_LOCKED) { - *status = 0x1f; - get_snr(fe); - } - return stat; -} - -static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on) -{ - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - - memset(&cmd, 0, sizeof(cmd)); - cmd.tuner = state->tuner; - cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE; - return mci_cmd(state, &cmd, NULL); -} - -static int stop(struct dvb_frontend *fe) -{ - struct mci *state = fe->demodulator_priv; - struct mci_command cmd; - u32 input = state->tuner; - - memset(&cmd, 0, sizeof(cmd)); - if (state->demod != 0xff) { - cmd.command = MCI_CMD_STOP; - cmd.demod = state->demod; - mci_cmd(state, &cmd, NULL); - if (state->base->iq_mode) { - cmd.command = MCI_CMD_STOP; - cmd.demod = state->demod; - cmd.output = 0; - mci_cmd(state, &cmd, NULL); - mci_config(state, SX8_TSCONFIG_MODE_NORMAL); - } - } - mutex_lock(&state->base->tuner_lock); - state->base->tuner_use_count[input]--; - if (!state->base->tuner_use_count[input]) - mci_set_tuner(fe, input, 0); - state->base->demod_in_use[state->demod] = 0; - state->base->used_ldpc_bitrate[state->nr] = 0; - state->demod = 0xff; - state->base->assigned_demod[state->nr] = 0xff; - state->base->iq_mode = 0; - mutex_unlock(&state->base->tuner_lock); - state->started = 0; - return 0; -} - -static int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); - u32 used_ldpc_bitrate = 0, free_ldpc_bitrate; - u32 used_demods = 0; - struct mci_command cmd; - u32 input = state->tuner; - u32 bits_per_symbol = 0; - int i, stat = 0; - - if (p->symbol_rate >= MCLK / 2) - flags &= ~1; - if ((flags & 3) == 0) - return -EINVAL; - - if (flags & 2) { - u32 tmp = modmask; - - bits_per_symbol = 1; - while (tmp & 1) { - tmp >>= 1; - bits_per_symbol++; - } - } - - mutex_lock(&state->base->tuner_lock); - if (state->base->iq_mode) { - stat = -EBUSY; - goto unlock; - } - for (i = 0; i < 8; i++) { - used_ldpc_bitrate += state->base->used_ldpc_bitrate[i]; - if (state->base->demod_in_use[i]) - used_demods++; - } - if ((used_ldpc_bitrate >= MAX_LDPC_BITRATE) || - ((ts_config & SX8_TSCONFIG_MODE_MASK) > - SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) { - stat = -EBUSY; - goto unlock; - } - free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate; - if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE) - free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE; - - while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate) - bits_per_symbol--; - if (bits_per_symbol < 2) { - stat = -EBUSY; - goto unlock; - } - i = (p->symbol_rate > MCLK / 2) ? 3 : 7; - while (i >= 0 && state->base->demod_in_use[i]) - i--; - if (i < 0) { - stat = -EBUSY; - goto unlock; - } - state->base->demod_in_use[i] = 1; - state->base->used_ldpc_bitrate[state->nr] = p->symbol_rate * bits_per_symbol; - state->demod = state->base->assigned_demod[state->nr] = i; - - if (!state->base->tuner_use_count[input]) - mci_set_tuner(fe, input, 1); - state->base->tuner_use_count[input]++; - state->base->iq_mode = (ts_config > 1); -unlock: - mutex_unlock(&state->base->tuner_lock); - if (stat) - return stat; - memset(&cmd, 0, sizeof(cmd)); - - if (state->base->iq_mode) { - cmd.command = SX8_CMD_ENABLE_IQOUTPUT; - cmd.demod = state->demod; - cmd.output = 0; - mci_cmd(state, &cmd, NULL); - mci_config(state, ts_config); - } - if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000) - flags |= 0x80; - printk("frontend %u: tuner=%u demod=%u\n", state->nr, state->tuner, state->demod); - cmd.command = MCI_CMD_SEARCH_DVBS; - cmd.dvbs2_search.flags = flags; - cmd.dvbs2_search.s2_modulation_mask = modmask & ((1 << (bits_per_symbol - 1)) - 1); - cmd.dvbs2_search.retry = 2; - cmd.dvbs2_search.frequency = p->frequency * 1000; - cmd.dvbs2_search.symbol_rate = p->symbol_rate; - cmd.dvbs2_search.scrambling_sequence_index = - p->scrambling_sequence_index; - cmd.dvbs2_search.input_stream_id = p->stream_id; - cmd.tuner = state->tuner; - cmd.demod = state->demod; - cmd.output = state->nr; - if (p->stream_id == 0x80000000) - cmd.output |= 0x80; - stat = mci_cmd(state, &cmd, NULL); - if (stat) - stop(fe); - return stat; -} - - -static int start_iq(struct dvb_frontend *fe, u32 ts_config) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 used_demods = 0; - struct mci_command cmd; - u32 input = state->tuner; - int i, stat = 0; - - mutex_lock(&state->base->tuner_lock); - if (state->base->iq_mode) { - stat = -EBUSY; - goto unlock; - } - for (i = 0; i < 8; i++) - if (state->base->demod_in_use[i]) - used_demods++; - if (used_demods > 0) { - stat = -EBUSY; - goto unlock; - } - state->demod = state->base->assigned_demod[state->nr] = 0; - if (!state->base->tuner_use_count[input]) - mci_set_tuner(fe, input, 1); - state->base->tuner_use_count[input]++; - state->base->iq_mode = (ts_config > 1); -unlock: - mutex_unlock(&state->base->tuner_lock); - if (stat) - return stat; - - memset(&cmd, 0, sizeof(cmd)); - cmd.command = SX8_CMD_START_IQ; - cmd.dvbs2_search.frequency = p->frequency * 1000; - cmd.dvbs2_search.symbol_rate = p->symbol_rate; - cmd.tuner = state->tuner; - cmd.demod = state->demod; - cmd.output = 7; - mci_config(state, ts_config); - stat = mci_cmd(state, &cmd, NULL); - if (stat) - stop(fe); - return stat; -} - -static int set_parameters(struct dvb_frontend *fe) -{ - int stat = 0; - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - u32 ts_config, iq_mode = 0, isi; - - if (state->started) - stop(fe); - isi = p->stream_id; - if (isi != NO_STREAM_ID_FILTER) { - iq_mode = (isi & 0x30000000) >> 28; - } - switch (iq_mode) { - case 1: - ts_config = (SX8_TSCONFIG_TSHEADER|SX8_TSCONFIG_MODE_IQ); - break; - case 2: - ts_config = (SX8_TSCONFIG_TSHEADER|SX8_TSCONFIG_MODE_IQ); - break; - default: - ts_config = SX8_TSCONFIG_MODE_NORMAL; - break; - } - if (iq_mode != 2) { - u32 flags = 3; - u32 mask = 3; - - if (p->modulation == APSK_16 || - p->modulation == APSK_32) { - flags = 2; - mask = 15; - } - stat = start(fe, flags, mask, ts_config); - } else { - stat = start_iq(fe, ts_config); - } - if (!stat) { - state->started = 1; - state->first_time_lock = 1; - state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL; - } - return stat; -} - -static int tune(struct dvb_frontend *fe, bool re_tune, - unsigned int mode_flags, - unsigned int *delay, enum fe_status *status) -{ - int r; - - if (re_tune) { - r = set_parameters(fe); - if (r) - return r; - } - r = read_status(fe, status); - if (r) - return r; - - if (*status & FE_HAS_LOCK) - return 0; - *delay = HZ / 10; - return 0; -} - -static int get_algo(struct dvb_frontend *fe) -{ - return DVBFE_ALGO_HW; -} - -static int set_input(struct dvb_frontend *fe, int input) -{ - struct mci *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; - - state->tuner = p->input = input; - printk("fe %u, input = %u\n", state->nr, input); - return 0; -} - -static int sleep(struct dvb_frontend *fe) -{ - return 0; -} - -static struct dvb_frontend_ops mci_ops = { - .delsys = { SYS_DVBS, SYS_DVBS2 }, - .info = { - .name = "DVB-S/S2X", - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 0, - .frequency_tolerance = 0, - .symbol_rate_min = 100000, - .symbol_rate_max = 100000000, - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | - FE_CAN_2G_MODULATION | - FE_CAN_MULTISTREAM, - }, - .get_frontend_algo = get_algo, - .tune = tune, - .release = release, - .read_status = read_status, - .set_input = set_input, - .sleep = sleep, -}; - static struct mci_base *match_base(void *key) { struct mci_base *p; @@ -607,16 +172,16 @@ static int probe(struct mci *state) return 0; } -struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, int mci_type, int nr) +struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg, int nr) { struct ddb_port *port = input->port; struct ddb *dev = port->dev; struct ddb_link *link = &dev->link[port->lnr]; struct mci_base *base; struct mci *state; - void *key = mci_type ? (void *) port : (void *) link; + void *key = cfg->type ? (void *) port : (void *) link; - state = kzalloc(sizeof(*state), GFP_KERNEL); + state = kzalloc(cfg->state_size, GFP_KERNEL); if (!state) return NULL; @@ -625,7 +190,7 @@ struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, int mci_type, int n base->count++; state->base = base; } else { - base = kzalloc(sizeof(*base), GFP_KERNEL); + base = kzalloc(cfg->base_size, GFP_KERNEL); if (!base) goto fail; base->key = key; @@ -641,14 +206,16 @@ struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, int mci_type, int n goto fail; } list_add(&base->mci_list, &mci_list); + if (cfg->base_init) + cfg->base_init(base); } - state->fe.ops = mci_ops; + memcpy(&state->fe.ops, cfg->fe_ops, sizeof(struct dvb_frontend_ops)); state->fe.demodulator_priv = state; state->nr = nr; - state->tuner = nr; state->demod = nr; - + if (cfg->init) + cfg->init(state); return &state->fe; fail: kfree(state); diff --git a/ddbridge/ddbridge-mci.h b/ddbridge/ddbridge-mci.h index d921c23..e1b098f 100644 --- a/ddbridge/ddbridge-mci.h +++ b/ddbridge/ddbridge-mci.h @@ -24,6 +24,22 @@ #ifndef _DDBRIDGE_MCI_H_ #define _DDBRIDGE_MCI_H_ +#define SX8_TSINPUT (0x280) +#define MIC_CONTROL (0x500) +#define MIC_PROGMEM_OLD (0x4000) +#define MIC_PROGMEM_OLD_SIZE (0x4000) + +#define MIC_PROGMEM (0x8000) +#define MIC_PROGMEM_SIZE (0x8000) + +#define MIC_DATAMEM (0x8000) +#define MIC_DATAMEM_SIZE (0x2000) + +#define MIC_INTERFACE_IN (0x0600) +#define MIC_INTERFACE_OUT (0x0680) +#define MIC_INTERFACE_VER (0x06F0) + + #define MCI_CONTROL (0x500) #define MCI_COMMAND (0x600) #define MCI_RESULT (0x680) @@ -52,10 +68,21 @@ #define SX8_TSCONFIG_BURSTSIZE_8K (0x00000020) #define SX8_TSCONFIG_BURSTSIZE_16K (0x00000030) +/* additional TS input control bits on MaxSX8 DD01:0009 */ +#define TS_INPUT_CONTROL_SIZEMASK (0x00000030) +#define TS_INPUT_CONTROL_SIZE188 (0x00000000) +#define TS_INPUT_CONTROL_SIZE192 (0x00000010) +#define TS_INPUT_CONTROL_SIZE196 (0x00000020) + + +/********************************************************/ + #define SX8_DEMOD_STOPPED (0) #define SX8_DEMOD_IQ_MODE (1) #define SX8_DEMOD_WAIT_SIGNAL (2) #define SX8_DEMOD_WAIT_MATYPE (3) +#define SX8_DEMOD_WAIT_FEC (4) +#define SX8_DEMOD_WAIT_FEC_S1 (5) #define SX8_DEMOD_TIMEOUT (14) #define SX8_DEMOD_LOCKED (15) @@ -69,10 +96,24 @@ #define MCI_CMD_GETSIGNALINFO (0x03) #define MCI_CMD_RFPOWER (0x04) -#define MCI_CMD_SEARCH_DVBS (0x10) +#define MCI_CMD_SEARCH_DVBS (0x10) +#define MCI_CMD_SEARCH_DVBC (0x20) +#define MCI_CMD_SEARCH_DVBT (0x21) +#define MCI_CMD_SEARCH_DVBT2 (0x22) #define MCI_CMD_GET_IQSYMBOL (0x30) +#define MCI_BANDWIDTH_1_7MHZ (0) +#define MCI_BANDWIDTH_5MHZ (1) +#define MCI_BANDWIDTH_6MHZ (2) +#define MCI_BANDWIDTH_7MHZ (3) +#define MCI_BANDWIDTH_8MHZ (4) + +#define M4_MODE_SX (2) +#define M4_MODE_C (3) +#define M4_MODE_T (4) +#define M4_MODE_T2 (5) + #define SX8_CMD_INPUT_ENABLE (0x40) #define SX8_CMD_INPUT_DISABLE (0x41) #define SX8_CMD_START_IQ (0x42) @@ -80,9 +121,18 @@ #define SX8_CMD_ENABLE_IQOUTPUT (0x44) #define SX8_CMD_DISABLE_IQOUTPUT (0x45) -#define MCI_ERROR_UNSUPPORTED (0x80) +#define M4_CMD_GET_T2_L1INFO (0x50) -#define MCI_SUCCESS(status) (status < MCI_ERROR_UNSUPPORTED) +#define MCI_STATUS_OK (0x00) +#define MCI_STATUS_UNSUPPORTED (0x80) +#define MCI_STATUS_RETRY (0xFD) +#define MCI_STATUS_NOT_READY (0xFE) +#define MCI_STATUS_ERROR (0xFF) + +#define MCI_SUCCESS(status) ((status & MCI_STATUS_UNSUPPORTED) == 0) + + +/********************************************************/ #define SX8_CMD_DIAG_READ8 (0xE0) #define SX8_CMD_DIAG_READ32 (0xE1) @@ -102,69 +152,251 @@ struct mci_command { - union { - u32 command_word; - struct { - u8 command; - u8 tuner; - u8 demod; - u8 output; - }; - }; - union { - u32 params[31]; - struct { - u8 flags; - u8 s2_modulation_mask; - u8 rsvd1; - u8 retry; - u32 frequency; - u32 symbol_rate; - u8 input_stream_id; - u8 rsvd2[3]; - u32 scrambling_sequence_index; - } dvbs2_search; - }; + union { + u32 command_word; + struct { + u8 command; + u8 tuner; + u8 demod; + u8 output; + }; + }; + union { + u32 params[31]; + struct { + u8 flags; /* Bit 0: DVB-S Enabled, 1: DVB-S2 Enabled, 7: InputStreamID*/ + /* Bit 0 : QPSK, 1: 8PSK/8APSK, 2 : 16APSK, 3: 32APSK, 4: 64APSK, 5: 128APSK, 6: 256APSK */ + u8 s2_modulation_mask; + u8 rsvd1; + u8 retry; + u32 frequency; + u32 symbol_rate; + u8 input_stream_id; + u8 rsvd2[3]; + u32 scrambling_sequence_index; + u32 frequency_range; + } dvbs2_search; + struct { + uint8_t flags; + uint8_t bandwidth; + uint8_t rsvd1; + uint8_t retry; + uint32_t frequency; + } dvbc_search; + + struct { + uint8_t flags; /* Bit 0: LP Stream */ + uint8_t bandwidth; + uint8_t rsvd1; + uint8_t retry; + uint32_t frequency; + } dvbt_Search; + + struct { + uint8_t flags; // Bit 0: T2 Lite Profile, 7: PLP, + uint8_t bandwidth; + uint8_t rsvd1; + uint8_t retry; + uint32_t frequency; + uint32_t reserved; + uint8_t plp; + uint8_t rsvd2[3]; + } dvbt2_Search; + + struct { + uint8_t Tap; + uint8_t Rsvd; + uint16_t Point; + } get_iq_symbol; + + struct { + uint8_t flags; // Bit 0 : 0 = VTM, 1 = SCAN. Bit 1: Set Gain + uint8_t roll_off; // + uint8_t rsvd1; + uint8_t rsvd2; + uint32_t frequency; + uint32_t symbol_rate; // Only in VTM mode. + uint16_t gain; + } sx8_start_iq; + + struct { + uint8_t flags; // Bit 1:0 = STVVGLNA Gain. 0 = AGC, 1 = 0dB, 2 = Minimum, 3 = Maximum + } sx8_input_enable; + }; }; struct mci_result { union { u32 status_word; struct { - u8 status; - u8 rsvd; + u8 status; + u8 mode; u16 time; }; }; union { u32 result[27]; struct { - u8 standard; - u8 pls_code; /* puncture rate for DVB-S */ - u8 roll_off; /* 7-6: rolloff, 5-2: rsrvd, 1:short, 0:pilots */ + u8 standard; // 1 = DVB-S, 2 = DVB-S2X + u8 pls_code; /* puncture rate for DVB-S */ + u8 roll_off; // 2-0: rolloff u8 rsvd; - u32 frequency; - u32 symbol_rate; - s16 channel_power; - s16 band_power; - s16 signal_to_noise; + u32 frequency; // actual frequency in Hz + u32 symbol_rate; // actual symbolrate in Hz + s16 channel_power; // channel power in dBm x 100 + s16 band_power; // band power in dBm x 100 + s16 signal_to_noise; // SNR in dB x 100, Note: negativ values are valid in DVB-S2 s16 rsvd2; - u32 packet_errors; - u32 ber_numerator; + u32 packet_errors; // Counter for packet errors. (set to 0 on Start command) + u32 ber_numerator; // Bit error rate: PreRS in DVB-S, PreBCH in DVB-S2X u32 ber_denominator; } dvbs2_signal_info; struct { - s16 I; - s16 Q; - } dvbs2_signal_iq; + u8 modulation; + u8 rsvd1[3]; + u32 frequency; // actual frequency in Hz + u32 symbol_rate; // actual symbolrate in Hz + s16 channel_power; // channel power in dBm x 100 + s16 band_power; // band power in dBm x 100 + s16 signal_to_noise; // SNR in dB x 100, Note: negativ values are valid in DVB-S2 + s16 rsvd2; + u32 packet_errors; // Counter for packet errors. (set to 0 on Start command) + u32 ber_numerator; // Bit error rate: PreRS in DVB-S, PreBCH in DVB-S2X + u32 ber_denominator; + } dvbc_signal_info; + struct { + u8 tps_25_32; // Constellation (2), Hierarchy (3), Coderate HP (3) + u8 tps_33_39; // Coderate LP (3), Guardinterval (2), FFT (2), 0 (1) + u16 tps_cell_id; // Cell Identifier + u32 frequency; // actual frequency in Hz + u32 rsvd1; // + s16 channel_power; // channel power in dBm x 100 + s16 band_power; // band power in dBm x 100 + s16 signal_to_noise; // SNR in dB x 100, Note: negativ values are valid in DVB-S2 + s16 rsvd2; + u32 packet_errors; // Counter for packet errors. (set to 0 on Start command) + u32 ber_numerator; // Bit error rate: PreRS in DVB-S, PreBCH in DVB-S2X + u32 ber_denominator; + } dvbt_signal_info; + struct { + u32 Rsvd0 ; // + u32 frequency; // actual frequency in Hz + u32 rsvd1; // + s16 channel_power; // channel power in dBm x 100 + s16 band_power; // band power in dBm x 100 + s16 signal_to_noise; // SNR in dB x 100, Note: negativ values are valid in DVB-S2 + s16 rsvd2; + u32 packet_errors; // Counter for packet errors. (set to 0 on Start command) + u32 ber_numerator; // Bit error rate: PreRS in DVB-S, PreBCH in DVB-S2X + u32 ber_denominator; + } dvbt2_signal_info; + struct { + s16 i; + s16 q; + } iq_symbol; + struct { + u8 t2_l1_pre[37]; + u8 t2_l1_post[15]; + u8 t2_l1_post_d[19]; + u8 t2_l1_post_c[19]; + } dvbt2_l1_info; }; u32 version[4]; }; -struct mci_cfg { +// Helper Macros + +// DVB-T2 L1-Pre Signalling Data ( ETSI EN 302 755 V1.4.1 Chapter 7.2.2 ) + +#define L1PRE_TYPE(p) ((p)[0] & 0xFF) +#define L1PRE_BWT_EXT(p) ((p)[1] & 0x01) +#define L1PRE_S1(p) ((p)[2] & 0x07) +#define L1PRE_S2(p) ((p)[3] & 0x0F) +#define L1PRE_L1_REPETITION_FLAG(p) ((p)[4] & 0x01) +#define L1PRE_GUARD_INTERVAL(p) ((p)[5] & 0x07) +#define L1PRE_PAPR(p) ((p)[6] & 0x0F) +#define L1PRE_L1_MOD(p) ((p)[7] & 0x0F) +#define L1PRE_L1_COD(p) ((p)[8] & 0x03) +#define L1PRE_L1_FEC_TYPE(p) ((p)[9] & 0x03) +#define L1PRE_L1_POST_SIZE(p) (((u32)((p)[10] & 0x03) << 16) | ((u32)(p)[11] << 8) | (p)[12]) +#define L1PRE_L1_POST_INFO_SIZE(p) (((u32)((p)[13] & 0x03) << 16) | ((u32)(p)[14] << 8) | (p)[15]) +#define L1PRE_PILOT_PATTERN(p) ((p)[16] & 0x0F) +#define L1PRE_TX_ID_AVAILABILITY(p) ((p)[17] & 0xFF) +#define L1PRE_CELL_ID(p) (((u16)(p)[18] << 8) | (p)[19]) +#define L1PRE_NETWORK_ID(p) (((u16)(p)[20] << 8) | (p)[21]) +#define L1PRE_T2_SYSTEM_ID(p) (((u16)(p)[22] << 8) | (p)[23]) +#define L1PRE_NUM_T2_FRAMES(p) ((p)[24] & 0xFF) +#define L1PRE_NUM_DATA_SYMBOLS(p) (((u16)((p)[25] & 0x0F) << 8) | (p)[26]) +#define L1PRE_REGEN_FLAG(p) ((p)[27] & 0x07) +#define L1PRE_L1_POST_EXTENSION(p) ((p)[28] & 0x01) +#define L1PRE_NUM_RF(p) ((p)[29] & 0x07) +#define L1PRE_CURRENT_RF_IDX(p) ((p)[30] & 0x07) +#define L1PRE_T2_VERSION(p) ((((p)[31] & 0x03) << 2) | (((p)[32] & 0xC0) >> 6)) +#define L1PRE_L1_POST_SCRAMBLED(p) (((p)[32] & 0x20) >> 5) +#define L1PRE_T2_BASE_LITE(p) (((p)[32] & 0x10) >> 4) +// DVB-T2 L1-Post Signalling Data ( ETSI EN 302 755 V1.4.1 Chapter 7.2.3 ) + +#define L1POST_SUB_SLICES_PER_FRAME(p) (((u16)(p)[ 0] & 0x7F) | (p)[ 1]) +#define L1POST_NUM_PLP(p) ((p)[2] & 0xFF) +#define L1POST_NUM_AUX(p) ((p)[3] & 0x0F) +#define L1POST_AUX_CONFIG_RFU(p) ((p)[4] & 0xFF) +#define L1POST_RF_IDX(p) ((p)[5] & 0x07) +#define L1POST_FREQUENCY(p) (((u32)(p)[6] << 24) | ((u32)(p)[7] << 16) | ((u32)(p)[8] << 8) | (p)[9]) +#define L1POST_FEF_TYPE(p) ((p)[10] & 0x0F) +#define L1POST_FEF_LENGTH(p) (((u32)(p)[11] << 16) | ((u32)(p)[12] << 8) | (p)[13]) +#define L1POST_FEF_INTERVAL(p) ((p)[14] & 0xFF) + +// Repeated for each PLP, +// Hardware is restricted to retrieve only values for current data PLP and common PLP + +#define L1POST_PLP_ID(p) ((p)[0] & 0xFF) +#define L1POST_PLP_TYPE(p) ((p)[1] & 0x07) +#define L1POST_PLP_PAYLOAD_TYPE(p) ((p)[2] & 0x1F) +#define L1POST_FF_FLAG(p) ((p)[3] & 0x01) +#define L1POST_FIRST_RF_IDX(p) ((p)[4] & 0x07) +#define L1POST_FIRST_FRAME_IDX(p) ((p)[5] & 0xFF) +#define L1POST_PLP_GROUP_ID(p) ((p)[6] & 0xFF) +#define L1POST_PLP_COD(p) ((p)[7] & 0x07) +#define L1POST_PLP_MOD(p) ((p)[8] & 0x07) +#define L1POST_PLP_ROTATION(p) ((p)[9] & 0x01) +#define L1POST_PLP_FEC_TYPE(p) ((p)[10] & 0x03) +#define L1POST_PLP_NUM_BLOCKS_MAX(p) (((u16)((p)[11] & 0x03) << 8) | (p)[12]) +#define L1POST_FRAME_INTERVAL(p) ((p)[13] & 0xFF) +#define L1POST_TIME_IL_LENGTH(p) ((p)[14] & 0xFF) +#define L1POST_TIME_IL_TYPE(p) ((p)[15] & 0x01) +#define L1POST_IN_BAND_A_FLAG(p) ((p)[16] & 0x01) +#define L1POST_IN_BAND_B_FLAG(p) (((p)[17] >> 7) & 0x01) +#define L1POST_RESERVED_1(p) (((u16)((p)[17] & 0x7F) << 4) | ((p)[18] & 0xF0) >> 4) +#define L1POST_PLP_MODE(p) (((p)[18] >> 2) & 0x03) +#define L1POST_STATIC_FLAG(p) (((p)[18] >> 1) & 0x01) +#define L1POST_STATIC_PADDING_FLAG(p) (((p)[18] >> 1) & 0x01) + +struct mci_base { + struct list_head mci_list; + void *key; + struct ddb_link *link; + struct completion completion; + struct i2c_adapter *i2c; + struct mutex i2c_lock; + struct mutex tuner_lock; + struct mutex mci_lock; + int count; + int type; }; -struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, int mci_type, int nr); +struct mci { + struct mci_base *base; + struct dvb_frontend fe; + int nr; + int demod; + int tuner; +}; + +int ddb_mci_cmd(struct mci *state, struct mci_command *command, struct mci_result *result); +int ddb_mci_cmd_raw(struct mci *state, struct mci_command *command, u32 command_len, + struct mci_result *result, u32 result_len); +int ddb_mci_config(struct mci *state, u32 config); + #endif diff --git a/ddbridge/ddbridge-regs.h b/ddbridge/ddbridge-regs.h index 7ac9f99..c648b02 100644 --- a/ddbridge/ddbridge-regs.h +++ b/ddbridge/ddbridge-regs.h @@ -21,12 +21,16 @@ /* Register Definitions */ -#define CUR_REGISTERMAP_VERSION_V1 0x00010001 -#define CUR_REGISTERMAP_VERSION_V2 0x00020000 -#define CUR_REGISTERMAP_VERSION_022X 0x00020001 +#define CUR_REGISTERMAP_VERSION 0x10004 +#define CUR_REGISTERMAP_VERSION_0007 0x10002 +#define CUR_REGISTERMAP_VERSION_0008 0x10002 +#define CUR_REGISTERMAP_VERSION_CI 0x10000 +#define CUR_REGISTERMAP_VERSION_CI_PRO 0x10000 -#define HARDWARE_VERSION 0x00000000 -#define REGISTERMAP_VERSION 0x00000004 +#define HARDWARE_VERSION 0x0000 +#define REGISTERMAP_VERSION 0x0004 +#define DEVICE_ID 0x0008 +#define BOARD_ID 0x000C /* ------------------------------------------------------------------------- */ /* SPI Controller */ @@ -44,13 +48,6 @@ /* ------------------------------------------------------------------------- */ /* MDIO */ -#if 0 -#define MDIO_CTRL 0x20 -#define MDIO_ADR 0x24 -#define MDIO_REG 0x28 -#define MDIO_VAL 0x2C -#endif - #define MDIO_CTRL_OFF 0x00 #define MDIO_ADR_OFF 0x04 #define MDIO_REG_OFF 0x08 @@ -155,7 +152,9 @@ #define TEMPMON_CONTROL_SCAN (0x00000001) #define TEMPMON_CONTROL_AUTOSCAN (0x00000002) #define TEMPMON_CONTROL_INTENABLE (0x00000004) +#define TEMPMON_CONTROL_CLEAR (0x00000008) #define TEMPMON_CONTROL_OVERTEMP (0x00008000) +#define TEMPMON_STATUS_SHUTDOWN (0x00008000) /* Temperature in C x 256 */ #define TEMPMON_CORE (TEMPMON_BASE + 0x04) @@ -167,6 +166,9 @@ #define TEMPMON_FANPWM (0x00000F00) /* PWM speed in 10% steps */ #define TEMPMON_FANTACHO (0x000000FF) /* Rotations in 100/min steps */ +#define TEMPMON_INTERRUPT_V1 (24) +#define TEMPMON_INTERRUPT_V1_MASK (1<<24) + /* V1 Temperature Monitor * Temperature Monitor TEMPMON_CONTROL & 0x8000 == 0 : ( 2x LM75A @ 0x90,0x92 ) * Temperature Monitor TEMPMON_CONTROL & 0x8000 == 1 : @@ -191,6 +193,7 @@ /* SHORT Temperature in C x 256 (ADM1032 ext) */ #define TEMPMON2_DACCORE (TEMPMON_SENSOR2) + /* ------------------------------------------------------------------------- */ /* I2C Master Controller */ @@ -230,6 +233,10 @@ #define TS_CONTROL(_io) ((_io)->regs + 0x00) #define TS_CONTROL2(_io) ((_io)->regs + 0x04) +#define TS_INPUT_CONTROL_ENABLE (0x00000001) +#define TS_INPUT_CONTROL_RESET (0x00000002) +#define TS_INPUT_CONTROL_SKIPERROR (0x00000008) + /* ------------------------------------------------------------------------- */ /* DMA Buffer */ @@ -255,12 +262,19 @@ #define LNB_BUSY BIT_ULL(4) #define LNB_TONE BIT_ULL(15) +#define LNB_INTERRUPT_BASE 4 + #define LNB_STATUS(i) (LNB_BASE + (i) * 0x20 + 0x04) #define LNB_VOLTAGE(i) (LNB_BASE + (i) * 0x20 + 0x08) #define LNB_CONFIG(i) (LNB_BASE + (i) * 0x20 + 0x0c) #define LNB_BUF_LEVEL(i) (LNB_BASE + (i) * 0x20 + 0x10) #define LNB_BUF_WRITE(i) (LNB_BASE + (i) * 0x20 + 0x14) +#define LNB_SETTING(i) (LNB_BASE + (i) * 0x20 + 0x0c) +#define LNB_FIFO_LEVEL(i) (LNB_BASE + (i) * 0x20 + 0x10) +#define LNB_RESET_FIFO(i) (LNB_BASE + (i) * 0x20 + 0x10) +#define LNB_WRITE_FIFO(i) (LNB_BASE + (i) * 0x20 + 0x14) + /* ------------------------------------------------------------------------- */ /* CI Interface (only CI-Bridge) */ diff --git a/ddbridge/ddbridge-sx8.c b/ddbridge/ddbridge-sx8.c new file mode 100644 index 0000000..afc7cfe --- /dev/null +++ b/ddbridge/ddbridge-sx8.c @@ -0,0 +1,522 @@ +/* + * ddbridge-sx8.c: Digital Devices MAX SX8 driver + * + * Copyright (C) 2018 Digital Devices GmbH + * Marcus Metzler + * Ralph Metzler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * 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. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, point your browser to + * http://www.gnu.org/copyleft/gpl.html + */ + +#include "ddbridge.h" +#include "ddbridge-io.h" +#include "ddbridge-i2c.h" +#include "ddbridge-mci.h" + +static const u32 MCLK = (1550000000 / 12); +static const u32 MAX_LDPC_BITRATE = (720000000); +static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); + +#define SX8_TUNER_NUM 4 +#define SX8_DEMOD_NUM 8 +#define SX8_DEMOD_NONE 0xff + +struct sx8_base { + struct mci_base mci_base; + + u8 tuner_use_count[SX8_TUNER_NUM]; + u32 gain_mode[SX8_TUNER_NUM]; + + u32 used_ldpc_bitrate[SX8_DEMOD_NUM]; + u8 demod_in_use[SX8_DEMOD_NUM]; + u32 iq_mode; + u32 burst_size; + u32 direct_mode; +}; + +struct sx8 { + struct mci mci; + + int first_time_lock; + int started; + struct mci_result signal_info; + + u32 bb_mode; + u32 local_frequency; + +}; + +static const u8 dvbs2_bits_per_symbol[] = { + 0, 0, 0, 0, + /* S2 QPSK */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + /* S2 8PSK */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + /* S2 16APSK */ + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + /* S2 32APSK */ + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 3, 0, 4, 0, + 2, 2, 2, 2, 2, 2, // S2X QPSK + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // S2X 8PSK + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // S2X 16APSK + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // S2X 32APSK + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // S2X 64APSK + 7, 7, 7, 7, // S2X 128APSK + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // S2X 256APSK + + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // S2X QPSK + 3, 3, 3, 3, 3, 3, 3, 3, // S2X 8PSK + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // S2X 16APSK + 5, 5, 5, 5, // S2X 32APSK + + 3, 4, 5, 6, 8, 10, +}; + + +static void release(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + + mci_base->count--; + if (mci_base->count == 0) { + list_del(&mci_base->mci_list); + kfree(mci_base); + } + kfree(state); +} + +static int get_info(struct dvb_frontend *fe) +{ + int stat; + struct sx8 *state = fe->demodulator_priv; + struct mci_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.command = MCI_CMD_GETSIGNALINFO; + cmd.demod = state->mci.demod; + stat = ddb_mci_cmd(&state->mci, &cmd, &state->signal_info); + return stat; +} + +static int get_snr(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = (s64) state->signal_info.dvbs2_signal_info.signal_to_noise * 10; + return 0; +} + +static int get_strength(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + s32 str; + + str = 100000 - (state->signal_info.dvbs2_signal_info.channel_power * 10 + 108750); + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + p->strength.stat[0].svalue = str; + return 0; +} + +static int read_status(struct dvb_frontend *fe, enum fe_status *status) +{ + int stat; + struct sx8 *state = fe->demodulator_priv; + struct mci_command cmd; + u32 val; + struct mci_result *res = (struct mci_result *)&val; + + cmd.command = MCI_CMD_GETSTATUS; + cmd.demod = state->mci.demod; + stat = ddb_mci_cmd_raw(&state->mci, &cmd, 1, res, 1); + if (stat) + return stat; + *status = 0x00; + get_info(fe); + get_strength(fe); + if (res->status == SX8_DEMOD_WAIT_MATYPE) + *status = 0x0f; + if (res->status == SX8_DEMOD_LOCKED) { + *status = 0x1f; + get_snr(fe); + } + return stat; +} + +static int mci_set_tuner(struct dvb_frontend *fe, u32 tuner, u32 on) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *) mci_base; + struct mci_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.tuner = state->mci.tuner; + cmd.command = on ? SX8_CMD_INPUT_ENABLE : SX8_CMD_INPUT_DISABLE; + cmd.sx8_input_enable.flags = sx8_base->gain_mode[state->mci.tuner]; + return ddb_mci_cmd(&state->mci, &cmd, NULL); +} + +static int stop(struct dvb_frontend *fe) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *) mci_base; + struct mci_command cmd; + u32 input = state->mci.tuner; + + memset(&cmd, 0, sizeof(cmd)); + if (state->mci.demod != SX8_DEMOD_NONE) { + cmd.command = MCI_CMD_STOP; + cmd.demod = state->mci.demod; + ddb_mci_cmd(&state->mci, &cmd, NULL); + if (sx8_base->iq_mode) { + cmd.command = SX8_CMD_DISABLE_IQOUTPUT; + cmd.demod = state->mci.demod; + cmd.output = 0; + ddb_mci_cmd(&state->mci, &cmd, NULL); + ddb_mci_config(&state->mci, SX8_TSCONFIG_MODE_NORMAL); + } + } + mutex_lock(&mci_base->tuner_lock); + sx8_base->tuner_use_count[input]--; + if (!sx8_base->tuner_use_count[input]) + mci_set_tuner(fe, input, 0); + if (state->mci.demod != SX8_DEMOD_NONE) { + sx8_base->demod_in_use[state->mci.demod] = 0; + state->mci.demod = SX8_DEMOD_NONE; + } + sx8_base->used_ldpc_bitrate[state->mci.nr] = 0; + sx8_base->iq_mode = 0; + mutex_unlock(&mci_base->tuner_lock); + state->started = 0; + return 0; +} + +static int start(struct dvb_frontend *fe, u32 flags, u32 modmask, u32 ts_config) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *) mci_base; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + static const u32 MAX_DEMOD_LDPC_BITRATE = (1550000000 / 6); + u32 used_ldpc_bitrate = 0, free_ldpc_bitrate; + u32 used_demods = 0; + struct mci_command cmd; + u32 input = state->mci.tuner; + u32 bits_per_symbol = 0; + int i = -1, stat = 0; + + if (p->symbol_rate >= MCLK / 2) + flags &= ~1; + if ((flags & 3) == 0) + return -EINVAL; + + if (flags & 2) { + u32 tmp = modmask; + + bits_per_symbol = 1; + while (tmp & 1) { + tmp >>= 1; + bits_per_symbol++; + } + } + + mutex_lock(&mci_base->tuner_lock); + if (sx8_base->iq_mode) { + stat = -EBUSY; + goto unlock; + } + + if (sx8_base->direct_mode) { + if (p->symbol_rate >= MCLK / 2) { + if (state->mci.nr < 4) + i = state->mci.nr; + } else { + i = state->mci.nr; + } + } else { + for (i = 0; i < SX8_DEMOD_NUM; i++) { + used_ldpc_bitrate += sx8_base->used_ldpc_bitrate[i]; + if (sx8_base->demod_in_use[i]) + used_demods++; + } + if ((used_ldpc_bitrate >= MAX_LDPC_BITRATE) || + ((ts_config & SX8_TSCONFIG_MODE_MASK) > + SX8_TSCONFIG_MODE_NORMAL && used_demods > 0)) { + stat = -EBUSY; + goto unlock; + } + free_ldpc_bitrate = MAX_LDPC_BITRATE - used_ldpc_bitrate; + if (free_ldpc_bitrate > MAX_DEMOD_LDPC_BITRATE) + free_ldpc_bitrate = MAX_DEMOD_LDPC_BITRATE; + + while (p->symbol_rate * bits_per_symbol > free_ldpc_bitrate) + bits_per_symbol--; + if (bits_per_symbol < 2) { + stat = -EBUSY; + goto unlock; + } + + modmask &= ((1 << (bits_per_symbol - 1)) - 1); + if( ((flags & 0x02) != 0) && (modmask == 0)) { + stat = -EBUSY; + goto unlock; + } + + i = (p->symbol_rate > MCLK / 2) ? 3 : 7; + while (i >= 0 && sx8_base->demod_in_use[i]) + i--; + } + if (i < 0) { + stat = -EBUSY; + goto unlock; + } + sx8_base->demod_in_use[i] = 1; + sx8_base->used_ldpc_bitrate[state->mci.nr] = p->symbol_rate * bits_per_symbol; + state->mci.demod = i; + + if (!sx8_base->tuner_use_count[input]) + mci_set_tuner(fe, input, 1); + sx8_base->tuner_use_count[input]++; + sx8_base->iq_mode = (ts_config > 1); +unlock: + mutex_unlock(&mci_base->tuner_lock); + if (stat) + return stat; + memset(&cmd, 0, sizeof(cmd)); + + if (sx8_base->iq_mode) { + cmd.command = SX8_CMD_ENABLE_IQOUTPUT; + cmd.demod = state->mci.demod; + cmd.output = 0; + ddb_mci_cmd(&state->mci, &cmd, NULL); + ddb_mci_config(&state->mci, ts_config); + } + if (p->stream_id != NO_STREAM_ID_FILTER && p->stream_id != 0x80000000) + flags |= 0x80; + printk("frontend %u: tuner=%u demod=%u\n", state->mci.nr, state->mci.tuner, state->mci.demod); + cmd.command = MCI_CMD_SEARCH_DVBS; + cmd.dvbs2_search.flags = flags; + cmd.dvbs2_search.s2_modulation_mask = modmask; + cmd.dvbs2_search.retry = 2; + cmd.dvbs2_search.frequency = p->frequency * 1000; + cmd.dvbs2_search.symbol_rate = p->symbol_rate; + cmd.dvbs2_search.scrambling_sequence_index = + p->scrambling_sequence_index; + cmd.dvbs2_search.input_stream_id = p->stream_id; + cmd.tuner = state->mci.tuner; + cmd.demod = state->mci.demod; + cmd.output = state->mci.nr; + if (p->stream_id == 0x80000000) + cmd.output |= 0x80; + stat = ddb_mci_cmd(&state->mci, &cmd, NULL); + if (stat) + stop(fe); + return stat; +} + + +static int start_iq(struct dvb_frontend *fe, u32 flags, u32 roll_off, u32 ts_config) +{ + struct sx8 *state = fe->demodulator_priv; + struct mci_base *mci_base = state->mci.base; + struct sx8_base *sx8_base = (struct sx8_base *) mci_base; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 used_demods = 0; + struct mci_command cmd; + u32 input = state->mci.tuner; + int i, stat = 0; + + mutex_lock(&mci_base->tuner_lock); + if (sx8_base->iq_mode) { + stat = -EBUSY; + goto unlock; + } + for (i = 0; i < SX8_DEMOD_NUM; i++) + if (sx8_base->demod_in_use[i]) + used_demods++; + if (used_demods > 0) { + stat = -EBUSY; + goto unlock; + } + state->mci.demod = 0; + if (!sx8_base->tuner_use_count[input]) + mci_set_tuner(fe, input, 1); + sx8_base->tuner_use_count[input]++; + sx8_base->iq_mode = (ts_config > 1); +unlock: + mutex_unlock(&mci_base->tuner_lock); + if (stat) + return stat; + + memset(&cmd, 0, sizeof(cmd)); + cmd.command = SX8_CMD_START_IQ; + cmd.sx8_start_iq.flags = flags; + cmd.sx8_start_iq.roll_off = roll_off; + cmd.sx8_start_iq.frequency = p->frequency * 1000; + cmd.sx8_start_iq.symbol_rate = p->symbol_rate; + cmd.tuner = state->mci.tuner; + cmd.demod = state->mci.demod; + stat = ddb_mci_cmd(&state->mci, &cmd, NULL); + if (stat) + stop(fe); + ddb_mci_config(&state->mci, ts_config); + return stat; +} + +static int set_parameters(struct dvb_frontend *fe) +{ + int stat = 0; + struct sx8 *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 ts_config = SX8_TSCONFIG_MODE_NORMAL, iq_mode = 0, isi; + + if (state->started) + stop(fe); + isi = p->stream_id; + if (isi != NO_STREAM_ID_FILTER) { + iq_mode = (isi & 0x30000000) >> 28; + } + if (iq_mode) + ts_config = (SX8_TSCONFIG_TSHEADER | SX8_TSCONFIG_MODE_IQ); + if (iq_mode < 3) { + u32 flags = 3; + u32 mask = 0x7f; + + if (p->modulation == APSK_16 || + p->modulation == APSK_32) { + flags = 2; + mask = 0x0f; + } + stat = start(fe, flags, mask, ts_config); + } else { + u32 flags = (iq_mode == 2) ? 1 : 0; + + stat = start_iq(fe, flags, 4, ts_config); + } + if (!stat) { + state->started = 1; + state->first_time_lock = 1; + state->signal_info.status = SX8_DEMOD_WAIT_SIGNAL; + } + return stat; +} + +static int tune(struct dvb_frontend *fe, bool re_tune, + unsigned int mode_flags, + unsigned int *delay, enum fe_status *status) +{ + int r; + + if (re_tune) { + r = set_parameters(fe); + if (r) + return r; + } + r = read_status(fe, status); + if (r) + return r; + + if (*status & FE_HAS_LOCK) + return 0; + *delay = HZ / 10; + return 0; +} + +static int get_algo(struct dvb_frontend *fe) +{ + return DVBFE_ALGO_HW; +} + +static int set_input(struct dvb_frontend *fe, int input) +{ + struct sx8 *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + + if (input >= SX8_TUNER_NUM) + return -EINVAL; + state->mci.tuner = p->input = input; + printk("fe %u, input = %u\n", state->mci.nr, input); + return 0; +} + +static int sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static struct dvb_frontend_ops sx8_ops = { + .delsys = { SYS_DVBS, SYS_DVBS2 }, + .info = { + .name = "DVB-S/S2X", + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 0, + .frequency_tolerance = 0, + .symbol_rate_min = 100000, + .symbol_rate_max = 100000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_2G_MODULATION | + FE_CAN_MULTISTREAM, + }, + .get_frontend_algo = get_algo, + .tune = tune, + .release = release, + .read_status = read_status, + .set_input = set_input, + .sleep = sleep, +}; + +static int init(struct mci *mci) +{ + struct sx8 *state = (struct sx8 *) mci; + + state->mci.demod = SX8_DEMOD_NONE; + return 0; +} + +static int base_init(struct mci_base *mci_base) +{ + //struct sx8_base *base = (struct sx8_base *) mci_base; + + return 0; +} + +struct mci_cfg ddb_max_sx8_cfg = { + .type = 0, + .fe_ops = &sx8_ops, + .base_size = sizeof(struct sx8_base), + .state_size = sizeof(struct sx8), + .init = init, + .base_init = base_init, +}; diff --git a/ddbridge/ddbridge.h b/ddbridge/ddbridge.h index 7de4868..bbba7c5 100644 --- a/ddbridge/ddbridge.h +++ b/ddbridge/ddbridge.h @@ -92,6 +92,7 @@ #include "mxl5xx.h" #include "ddbridge-regs.h" +#include "ddbridge-mci.h" #define DDB_MAX_I2C 32 #define DDB_MAX_PORT 32 @@ -157,21 +158,24 @@ struct ddb_info { u32 version; char *name; u32 i2c_mask; + u32 board_control; + u32 board_control_2; + u8 port_num; u8 led_num; u8 fan_num; u8 temp_num; u8 temp_bus; - u32 board_control; - u32 board_control_2; u8 ns_num; u8 con_clock; /* use a continuous clock */ u8 ts_quirks; - u8 mci; #define TS_QUIRK_SERIAL 1 #define TS_QUIRK_REVERSED 2 #define TS_QUIRK_NO_OUTPUT 4 #define TS_QUIRK_ALT_OSC 8 + u8 mci_ports; + u8 mci_type; + u32 tempmon_irq; u32 lostlock_irq; u32 mdio_base; @@ -557,10 +561,21 @@ void ddb_i2c_release(struct ddb *dev); int ddb_ci_attach(struct ddb_port *port, u32 bitrate); int ddb_fe_attach_mxl5xx(struct ddb_input *input); -int ddb_fe_attach_mci(struct ddb_input *input); +int ddb_fe_attach_mci(struct ddb_input *input, u32 type); int ddb_lnb_init_fmode(struct ddb *dev, struct ddb_link *link, u32 fm); struct ddb_irq *ddb_irq_set(struct ddb *dev, u32 link, u32 nr, void (*handler)(void *), void *data); +struct mci_cfg { + int type; + struct dvb_frontend_ops *fe_ops; + u32 base_size; + u32 state_size; + int (*init)(struct mci *mci); + int (*base_init)(struct mci_base *mci_base); +}; + +struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, struct mci_cfg *cfg, int nr); + #endif