mirror of
				https://github.com/DigitalDevices/dddvb.git
				synced 2025-03-01 10:35:23 +00:00 
			
		
		
		
	add first basic support for MCI frontends like the MAXSX8 (incomplete)
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										519
									
								
								ddbridge/ddbridge-mci.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										519
									
								
								ddbridge/ddbridge-mci.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,519 @@ | ||||
| /* | ||||
|  * ddbridge-mci.c: Digital Devices microcode interface | ||||
|  * | ||||
|  * Copyright (C) 2017 Digital Devices GmbH | ||||
|  *                    Ralph Metzler <rjkm@metzlerbros.de> | ||||
|  *                    Marcus Metzler <mocm@metzlerbros.de> | ||||
|  * | ||||
|  * 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; | ||||
| } | ||||
|  | ||||
							
								
								
									
										142
									
								
								ddbridge/ddbridge-mci.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								ddbridge/ddbridge-mci.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | ||||
| /* | ||||
|  * ddbridge-mci.h: Digital Devices micro code interface | ||||
|  * | ||||
|  * Copyright (C) 2017 Digital Devices GmbH | ||||
|  *                    Marcus Metzler <mocm@metzlerbros.de> | ||||
|  *                    Ralph Metzler <rjkm@metzlerbros.de> | ||||
|  * | ||||
|  * 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 | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user