diff --git a/ddbridge/Kbuild b/ddbridge/Kbuild index 7deaee3..28e2508 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 -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 -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-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 obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o obj-$(CONFIG_DVB_OCTONET) += octonet.o diff --git a/ddbridge/Makefile b/ddbridge/Makefile index 11dac0c..42120ee 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 -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-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 obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o obj-$(CONFIG_DVB_OCTONET) += octonet.o diff --git a/ddbridge/Makefile.kernel b/ddbridge/Makefile.kernel index ce59054..401f6d9 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 -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-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 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 69e3642..786388c 100644 --- a/ddbridge/ddbridge-core.c +++ b/ddbridge/ddbridge-core.c @@ -26,6 +26,7 @@ #include "ddbridge-i2c.h" #include "ddbridge-io.h" #include "dvb_net.h" +#include "ddbridge-mci.h" struct workqueue_struct *ddb_wq; @@ -1748,6 +1749,10 @@ 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) + return -ENODEV; + break; default: return 0; } @@ -2038,6 +2043,16 @@ 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) + return; + port->name = "DUAL MCI"; + port->type_name = "MCI"; + port->class = DDB_PORT_TUNER; + port->type = DDB_TUNER_MCI; + return; + } + if (port->nr > 1 && dev->link[l].info->type == DDB_OCTOPUS_CI) { port->name = "CI internal"; port->type_name = "INTERNAL"; @@ -2658,6 +2673,7 @@ static void ddb_ports_init(struct ddb *dev) break; case DDB_OCTOPUS_MAX: case DDB_OCTOPUS_MAX_CT: + case DDB_OCTOPUS_MCI: ddb_input_init(port, 2 * i, 0, 2 * p); ddb_input_init(port, 2 * i + 1, 1, 2 * p + 1); break; @@ -4249,7 +4265,8 @@ static int ddb_gtl_init_link(struct ddb *dev, u32 l) link->info = get_ddb_info(id & 0xffff, id >> 16, subid & 0xffff, subid >> 16); if (link->info->type != DDB_OCTOPUS_MAX_CT && - link->info->type != DDB_OCTOPUS_MAX) { + link->info->type != DDB_OCTOPUS_MAX && + link->info->type != DDB_OCTOPUS_MCI ) { dev_info(dev->dev, "Detected GT link but found invalid ID %08x. You might have to update (flash) the add-on card first.", id); diff --git a/ddbridge/ddbridge-max.c b/ddbridge/ddbridge-max.c index dbcc1e1..ac18fe1 100644 --- a/ddbridge/ddbridge-max.c +++ b/ddbridge/ddbridge-max.c @@ -24,6 +24,7 @@ #include "ddbridge.h" #include "ddbridge-io.h" #include "ddbridge-i2c.h" +#include "ddbridge-mci.h" /* MAX LNB interface related module parameters */ @@ -442,3 +443,48 @@ int ddb_fe_attach_mxl5xx(struct ddb_input *input) return 0; } +/* MAX MCI related functions */ + +static struct mci_cfg maxsx8 = { + +}; + +int ddb_fe_attach_mci(struct ddb_input *input) +{ + struct ddb *dev = input->port->dev; + //struct i2c_adapter *i2c = &input->port->i2c->adap; + struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1]; + struct ddb_port *port = input->port; + struct ddb_link *link = &dev->link[port->lnr]; + int demod, tuner; + struct mci_cfg cfg; + + cfg = maxsx8; + demod = input->nr; + tuner = demod & 3; + if (fmode == 3) + tuner = 0; + dvb->fe = dvb_attach(ddb_mci_attach, input, 0, demod); + if (!dvb->fe) { + dev_err(dev->dev, "No MAXSX8 found!\n"); + return -ENODEV; + } + if (input->nr < 4) { + lnb_command(dev, port->lnr, input->nr, LNB_CMD_INIT); + lnb_set_voltage(dev, port->lnr, input->nr, SEC_VOLTAGE_OFF); + } + ddb_lnb_init_fmode(dev, link, fmode); + + dvb->fe->ops.set_voltage = max_set_voltage; + dvb->fe->ops.enable_high_lnb_voltage = max_enable_high_lnb_voltage; + dvb->fe->ops.set_tone = max_set_tone; + dvb->diseqc_send_master_cmd = dvb->fe->ops.diseqc_send_master_cmd; + dvb->fe->ops.diseqc_send_master_cmd = max_send_master_cmd; + dvb->fe->ops.diseqc_send_burst = max_send_burst; + dvb->fe->sec_priv = input; + dvb->set_input = dvb->fe->ops.set_input; + dvb->fe->ops.set_input = max_set_input; + dvb->input = tuner; + return 0; +} + diff --git a/ddbridge/ddbridge-mci.c b/ddbridge/ddbridge-mci.c new file mode 100644 index 0000000..3d1948b --- /dev/null +++ b/ddbridge/ddbridge-mci.c @@ -0,0 +1,519 @@ +/* + * ddbridge-mci.c: Digital Devices microcode interface + * + * Copyright (C) 2017 Digital Devices GmbH + * Ralph Metzler + * Marcus 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-mci.h" + +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]; +}; + +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; +}; + + +static int mci_reset(struct mci *state) +{ + struct ddb_link *link = state->base->link; + u32 status = 0; + u32 timeout = 40; + + ddblwritel(link, MCI_CONTROL_RESET, MCI_CONTROL); + ddblwritel(link, 0, MCI_CONTROL + 4); /* 1= no internal init */ + msleep(300); + ddblwritel(link, 0, MCI_CONTROL); + + while(1) { + status = ddblreadl(link, MCI_CONTROL); + if ((status & MCI_CONTROL_READY) == MCI_CONTROL_READY) + break; + if (--timeout == 0) + break; + msleep(50); + } + if ((status & MCI_CONTROL_READY) == 0 ) + return -1; + if (link->ids.device == 0x0009) + ddblwritel(link, SX8_TSCONFIG_MODE_NORMAL, SX8_TSCONFIG); + return 0; +} + +static int _mci_cmd_unlocked(struct mci *state, + u32 *cmd, u32 cmd_len, + u32 *res, u32 res_len) +{ + struct ddb_link *link = state->base->link; + u32 i, val; + unsigned long stat; + + val = ddblreadl(link, MCI_CONTROL); + if (val & (MCI_CONTROL_RESET | MCI_CONTROL_START_COMMAND)) + return -EIO; + if (cmd && cmd_len) + for (i = 0; i < cmd_len; i++) + ddblwritel(link, cmd[i], MCI_COMMAND + i * 4); + val |= (MCI_CONTROL_START_COMMAND | MCI_CONTROL_ENABLE_DONE_INTERRUPT); + ddblwritel(link, val, MCI_CONTROL); + + stat = wait_for_completion_timeout(&state->base->completion, HZ); + if (stat == 0) { + printk("MCI timeout\n"); + return -EIO; + } + if (res && res_len) + for (i = 0; i < res_len; i++) + res[i] = ddblreadl(link, MCI_RESULT + i * 4); + return 0; +} + +static int 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)); +} + +static int 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, + (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, + 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); + mutex_unlock(&state->base->mci_lock); + return stat; +} + +static void mci_handler(void *priv) +{ + struct mci_base *base = (struct mci_base *)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; + + cmd.command = SX8_CMD_GETSIGNALINFO; + cmd.demod = state->demod; + stat = mci_cmd(state, &cmd, &state->signal_info); + return stat; +} + +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 = SX8_CMD_GETSTATUS; + cmd.demod = state->demod; + stat = _mci_cmd(state, &cmd, 1, res, 1); + if (stat) + return stat; + if (res->status == SX8_DEMOD_LOCKED) + *status = 0x1f; + else + *status = 0; + 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_TUNER_ENABLE : SX8_CMD_TUNER_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; + + if (state->demod != 0xff) { + memset(&cmd, 0, sizeof(cmd)); + cmd.demod = state->demod; + cmd.command = SX8_CMD_STOP; + mci_cmd(state, &cmd, NULL); + } + 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; + mutex_unlock(&state->base->tuner_lock); + state->started = 0; + return 0; +} + +static int start(struct dvb_frontend *fe) +{ + 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; + struct mci_command cmd; + u32 input = state->tuner; + u32 bits_per_symbol = 3; + int i, stat = 0; + + mutex_lock(&state->base->tuner_lock); + for (i = 0; i < 8; i++) + used_ldpc_bitrate += state->base->used_ldpc_bitrate[i]; + if (used_ldpc_bitrate >= MAX_LDPC_BITRATE) { + 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 > MAX_DEMOD_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]++; +unlock: + mutex_unlock(&state->base->tuner_lock); + if (stat) + return stat; + + printk("frontend %u: tuner=%u demod=%u\n", state->nr, state->tuner, state->demod); + cmd.command = SX8_CMD_SEARCH; + cmd.dvbs2_search.flags = 3; /* S1 + S2 */ +#if 1 + /* Enable all Modcods up to bits_per_symbol */ + cmd.dvbs2_search.s2_modulation_mask = (1 << (bits_per_symbol - 1)) - 1; +#else + /* Enable Modcods matching bits_per_symbol */ + cmd.dvbs2_search.s2_modulation_mask = (1 << (bits_per_symbol - 2)); +#endif + cmd.dvbs2_search.retry = 255; /* forever */ + 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 = state->nr; + stat = mci_cmd(state, &cmd, NULL); + return stat; +} + +static int set_parameters(struct dvb_frontend *fe) +{ + int stat = 0; + struct mci *state = fe->demodulator_priv; + + if (state->started) + stop(fe); + stat = start(fe); + 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) +{ + struct mci *state = fe->demodulator_priv; + +} + +static int get_snr(struct dvb_frontend *fe) +{ + struct mci *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + s32 snr; + + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].svalue = (s64) state->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 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 = 70000000, + .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, +}; + +static struct mci_base *match_base(void *key) +{ + struct mci_base *p; + + list_for_each_entry(p, &mci_list, mci_list) + if (p->key == key) + return p; + return NULL; +} + +static int probe(struct mci *state) +{ + mci_reset(state); + return 0; +} + +struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, int mci_type, 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; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + base = match_base(key); + if (base) { + base->count++; + state->base = base; + } else { + base = kzalloc(sizeof(*base), GFP_KERNEL); + if (!base) + goto fail; + base->key = key; + base->count = 1; + base->link = link; + mutex_init(&base->mci_lock); + mutex_init(&base->tuner_lock); + ddb_irq_set(dev, link->nr, 0, mci_handler, base); + init_completion(&base->completion); + state->base = base; + if (probe(state) < 0) { + kfree(base); + goto fail; + } + list_add(&base->mci_list, &mci_list); + } + state->fe.ops = mci_ops; + state->fe.demodulator_priv = state; + state->nr = nr; + + state->tuner = nr; + state->demod = nr; + + return &state->fe; +fail: + kfree(state); + return NULL; +} + diff --git a/ddbridge/ddbridge-mci.h b/ddbridge/ddbridge-mci.h new file mode 100644 index 0000000..7828a8f --- /dev/null +++ b/ddbridge/ddbridge-mci.h @@ -0,0 +1,142 @@ +/* + * ddbridge-mci.h: Digital Devices micro code interface + * + * Copyright (C) 2017 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 + */ + +#ifndef _DDBRIDGE_MCI_H_ +#define _DDBRIDGE_MCI_H_ + +#define MCI_CONTROL (0x500) +#define MCI_COMMAND (0x600) +#define MCI_RESULT (0x680) + +#define MCI_COMMAND_SIZE (0x80) +#define MCI_RESULT_SIZE (0x80) + +#define MCI_CONTROL_START_COMMAND (0x00000001) +#define MCI_CONTROL_ENABLE_DONE_INTERRUPT (0x00000002) +#define MCI_CONTROL_RESET (0x00008000) +#define MCI_CONTROL_READY (0x00010000) + +#define SX8_TSCONFIG (0x280) + +#define SX8_TSCONFIG_MODE_MASK (0x00000003) +#define SX8_TSCONFIG_MODE_OFF (0x00000000) +#define SX8_TSCONFIG_MODE_NORMAL (0x00000001) +#define SX8_TSCONFIG_MODE_IQ (0x00000003) + + +#define SX8_DEMOD_STOPPED (0) +#define SX8_DEMOD_WAIT_SIGNAL (2) +#define SX8_DEMOD_TIMEOUT (14) +#define SX8_DEMOD_LOCKED (15) + +#define SX8_CMD_TUNER_ENABLE (0x01) +#define SX8_CMD_TUNER_DISABLE (0x02) +#define SX8_CMD_SEARCH (0x03) +#define SX8_CMD_GETSTATUS (0x04) +#define SX8_CMD_STOP (0x05) +#define SX8_CMD_GETSIGNALINFO (0x07) + +#define SX8_CMD_SELECT_IQOUT (0x10) +#define SX8_CMD_SELECT_TSOUT (0x11) + +#define SX8_CMD_DIAG_READ8 (0xE0) +#define SX8_CMD_DIAG_READ32 (0xE1) +#define SX8_CMD_DIAG_WRITE8 (0xE2) +#define SX8_CMD_DIAG_WRITE32 (0xE3) + +#define SX8_CMD_DIAG_READV (0xE8) +#define SX8_CMD_DIAG_WRITEV (0xE9) + + +#define M4_CMD_DIAG_READX (0xE0) +#define M4_CMD_DIAG_READT (0xE1) +#define M4_CMD_DIAG_WRITEX (0xE2) +#define M4_CMD_DIAG_WRITET (0xE3) + +#define M4_CMD_DIAG_READRF (0xE8) +#define M4_CMD_DIAG_WRITERF (0xE9) + + +#define SX8_ERROR_UNSUPPORTED (0x80) +#define SX8_SUCCESS(status) (status < SX8_ERROR_UNSUPPORTED) + +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 rsvd; + u8 retry; + u32 frequency; + u32 symbol_rate; + } dvbs2_search; + }; +}; + +struct mci_result { + union { + u32 status_word; + struct { + u8 status; + u8 rsvd; + 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 rsvd; + u32 frequency; + u32 symbol_rate; + s16 channel_power; + s16 band_power; + s16 signal_to_noise; + s16 rsvd2; + u32 packet_errors; + u32 ber_numerator; + u32 ber_denominator; + } dvbs2_signal_info; + }; + u32 version[4]; +}; + +struct mci_cfg { + + +}; + +struct dvb_frontend *ddb_mci_attach(struct ddb_input *input, int mci_type, int nr); +#endif diff --git a/ddbridge/ddbridge.h b/ddbridge/ddbridge.h index 5d2fabe..9a48fda 100644 --- a/ddbridge/ddbridge.h +++ b/ddbridge/ddbridge.h @@ -144,15 +144,16 @@ struct ddb_ids { struct ddb_info { u32 type; -#define DDB_NONE 0 -#define DDB_OCTOPUS 1 -#define DDB_OCTOPUS_CI 2 -#define DDB_MOD 3 -#define DDB_OCTONET 4 -#define DDB_OCTOPUS_MAX 5 -#define DDB_OCTOPUS_MAX_CT 6 -#define DDB_OCTOPRO 7 -#define DDB_OCTOPRO_HDIN 8 +#define DDB_NONE 0 +#define DDB_OCTOPUS 1 +#define DDB_OCTOPUS_CI 2 +#define DDB_MOD 3 +#define DDB_OCTONET 4 +#define DDB_OCTOPUS_MAX 5 +#define DDB_OCTOPUS_MAX_CT 6 +#define DDB_OCTOPRO 7 +#define DDB_OCTOPRO_HDIN 8 +#define DDB_OCTOPUS_MCI 9 u32 version; char *name; u32 i2c_mask; @@ -173,6 +174,7 @@ struct ddb_info { #define TS_QUIRK_ALT_OSC 8 u32 tempmon_irq; u32 lostlock_irq; + u8 mci; const struct ddb_regmap *regmap; }; @@ -300,6 +302,7 @@ struct ddb_port { #define DDB_CI_EXTERNAL_XO2_B 13 #define DDB_TUNER_DVBS_STV0910_PR 14 #define DDB_TUNER_DVBC2T2I_SONY_P 15 +#define DDB_TUNER_MCI 16 #define DDB_TUNER_XO2 32 #define DDB_TUNER_DVBS_STV0910 (DDB_TUNER_XO2 + 0) @@ -550,8 +553,10 @@ 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_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); + #endif