162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ddbridge-max.c: Digital Devices bridge MAX card support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010-2017 Digital Devices GmbH
662306a36Sopenharmony_ci *                         Ralph Metzler <rjkm@metzlerbros.de>
762306a36Sopenharmony_ci *                         Marcus Metzler <mocm@metzlerbros.de>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/poll.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <linux/pci.h>
1862306a36Sopenharmony_ci#include <linux/pci_ids.h>
1962306a36Sopenharmony_ci#include <linux/timer.h>
2062306a36Sopenharmony_ci#include <linux/i2c.h>
2162306a36Sopenharmony_ci#include <linux/swab.h>
2262306a36Sopenharmony_ci#include <linux/vmalloc.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "ddbridge.h"
2562306a36Sopenharmony_ci#include "ddbridge-regs.h"
2662306a36Sopenharmony_ci#include "ddbridge-io.h"
2762306a36Sopenharmony_ci#include "ddbridge-mci.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "ddbridge-max.h"
3062306a36Sopenharmony_ci#include "mxl5xx.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/******************************************************************************/
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* MaxS4/8 related modparams */
3562306a36Sopenharmony_cistatic int fmode;
3662306a36Sopenharmony_cimodule_param(fmode, int, 0444);
3762306a36Sopenharmony_ciMODULE_PARM_DESC(fmode, "frontend emulation mode");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int fmode_sat = -1;
4062306a36Sopenharmony_cimodule_param(fmode_sat, int, 0444);
4162306a36Sopenharmony_ciMODULE_PARM_DESC(fmode_sat, "set frontend emulation mode sat");
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int old_quattro;
4462306a36Sopenharmony_cimodule_param(old_quattro, int, 0444);
4562306a36Sopenharmony_ciMODULE_PARM_DESC(old_quattro, "old quattro LNB input order ");
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/******************************************************************************/
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int lnb_command(struct ddb *dev, u32 link, u32 lnb, u32 cmd)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	u32 c, v = 0, tag = DDB_LINK_TAG(link);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	v = LNB_TONE & (dev->link[link].lnb.tone << (15 - lnb));
5462306a36Sopenharmony_ci	ddbwritel(dev, cmd | v, tag | LNB_CONTROL(lnb));
5562306a36Sopenharmony_ci	for (c = 0; c < 10; c++) {
5662306a36Sopenharmony_ci		v = ddbreadl(dev, tag | LNB_CONTROL(lnb));
5762306a36Sopenharmony_ci		if ((v & LNB_BUSY) == 0)
5862306a36Sopenharmony_ci			break;
5962306a36Sopenharmony_ci		msleep(20);
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	if (c == 10)
6262306a36Sopenharmony_ci		dev_info(dev->dev, "%s lnb = %08x  cmd = %08x\n",
6362306a36Sopenharmony_ci			 __func__, lnb, cmd);
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int max_send_master_cmd(struct dvb_frontend *fe,
6862306a36Sopenharmony_ci			       struct dvb_diseqc_master_cmd *cmd)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct ddb_input *input = fe->sec_priv;
7162306a36Sopenharmony_ci	struct ddb_port *port = input->port;
7262306a36Sopenharmony_ci	struct ddb *dev = port->dev;
7362306a36Sopenharmony_ci	struct ddb_dvb *dvb = &port->dvb[input->nr & 1];
7462306a36Sopenharmony_ci	u32 tag = DDB_LINK_TAG(port->lnr);
7562306a36Sopenharmony_ci	int i;
7662306a36Sopenharmony_ci	u32 fmode = dev->link[port->lnr].lnb.fmode;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (fmode == 2 || fmode == 1)
7962306a36Sopenharmony_ci		return 0;
8062306a36Sopenharmony_ci	if (dvb->diseqc_send_master_cmd)
8162306a36Sopenharmony_ci		dvb->diseqc_send_master_cmd(fe, cmd);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	mutex_lock(&dev->link[port->lnr].lnb.lock);
8462306a36Sopenharmony_ci	ddbwritel(dev, 0, tag | LNB_BUF_LEVEL(dvb->input));
8562306a36Sopenharmony_ci	for (i = 0; i < cmd->msg_len; i++)
8662306a36Sopenharmony_ci		ddbwritel(dev, cmd->msg[i], tag | LNB_BUF_WRITE(dvb->input));
8762306a36Sopenharmony_ci	lnb_command(dev, port->lnr, dvb->input, LNB_CMD_DISEQC);
8862306a36Sopenharmony_ci	mutex_unlock(&dev->link[port->lnr].lnb.lock);
8962306a36Sopenharmony_ci	return 0;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int lnb_send_diseqc(struct ddb *dev, u32 link, u32 input,
9362306a36Sopenharmony_ci			   struct dvb_diseqc_master_cmd *cmd)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	u32 tag = DDB_LINK_TAG(link);
9662306a36Sopenharmony_ci	int i;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	ddbwritel(dev, 0, tag | LNB_BUF_LEVEL(input));
9962306a36Sopenharmony_ci	for (i = 0; i < cmd->msg_len; i++)
10062306a36Sopenharmony_ci		ddbwritel(dev, cmd->msg[i], tag | LNB_BUF_WRITE(input));
10162306a36Sopenharmony_ci	lnb_command(dev, link, input, LNB_CMD_DISEQC);
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int lnb_set_sat(struct ddb *dev, u32 link, u32 input, u32 sat, u32 band,
10662306a36Sopenharmony_ci		       u32 hor)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct dvb_diseqc_master_cmd cmd = {
10962306a36Sopenharmony_ci		.msg = {0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00},
11062306a36Sopenharmony_ci		.msg_len = 4
11162306a36Sopenharmony_ci	};
11262306a36Sopenharmony_ci	cmd.msg[3] = 0xf0 | (((sat << 2) & 0x0c) | (band ? 1 : 0) |
11362306a36Sopenharmony_ci		(hor ? 2 : 0));
11462306a36Sopenharmony_ci	return lnb_send_diseqc(dev, link, input, &cmd);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic int lnb_set_tone(struct ddb *dev, u32 link, u32 input,
11862306a36Sopenharmony_ci			enum fe_sec_tone_mode tone)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	int s = 0;
12162306a36Sopenharmony_ci	u32 mask = (1ULL << input);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	switch (tone) {
12462306a36Sopenharmony_ci	case SEC_TONE_OFF:
12562306a36Sopenharmony_ci		if (!(dev->link[link].lnb.tone & mask))
12662306a36Sopenharmony_ci			return 0;
12762306a36Sopenharmony_ci		dev->link[link].lnb.tone &= ~(1ULL << input);
12862306a36Sopenharmony_ci		break;
12962306a36Sopenharmony_ci	case SEC_TONE_ON:
13062306a36Sopenharmony_ci		if (dev->link[link].lnb.tone & mask)
13162306a36Sopenharmony_ci			return 0;
13262306a36Sopenharmony_ci		dev->link[link].lnb.tone |= (1ULL << input);
13362306a36Sopenharmony_ci		break;
13462306a36Sopenharmony_ci	default:
13562306a36Sopenharmony_ci		s = -EINVAL;
13662306a36Sopenharmony_ci		break;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	if (!s)
13962306a36Sopenharmony_ci		s = lnb_command(dev, link, input, LNB_CMD_NOP);
14062306a36Sopenharmony_ci	return s;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int lnb_set_voltage(struct ddb *dev, u32 link, u32 input,
14462306a36Sopenharmony_ci			   enum fe_sec_voltage voltage)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	int s = 0;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (dev->link[link].lnb.oldvoltage[input] == voltage)
14962306a36Sopenharmony_ci		return 0;
15062306a36Sopenharmony_ci	switch (voltage) {
15162306a36Sopenharmony_ci	case SEC_VOLTAGE_OFF:
15262306a36Sopenharmony_ci		if (dev->link[link].lnb.voltage[input])
15362306a36Sopenharmony_ci			return 0;
15462306a36Sopenharmony_ci		lnb_command(dev, link, input, LNB_CMD_OFF);
15562306a36Sopenharmony_ci		break;
15662306a36Sopenharmony_ci	case SEC_VOLTAGE_13:
15762306a36Sopenharmony_ci		lnb_command(dev, link, input, LNB_CMD_LOW);
15862306a36Sopenharmony_ci		break;
15962306a36Sopenharmony_ci	case SEC_VOLTAGE_18:
16062306a36Sopenharmony_ci		lnb_command(dev, link, input, LNB_CMD_HIGH);
16162306a36Sopenharmony_ci		break;
16262306a36Sopenharmony_ci	default:
16362306a36Sopenharmony_ci		s = -EINVAL;
16462306a36Sopenharmony_ci		break;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci	dev->link[link].lnb.oldvoltage[input] = voltage;
16762306a36Sopenharmony_ci	return s;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int max_set_input_unlocked(struct dvb_frontend *fe, int in)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct ddb_input *input = fe->sec_priv;
17362306a36Sopenharmony_ci	struct ddb_port *port = input->port;
17462306a36Sopenharmony_ci	struct ddb *dev = port->dev;
17562306a36Sopenharmony_ci	struct ddb_dvb *dvb = &port->dvb[input->nr & 1];
17662306a36Sopenharmony_ci	int res = 0;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (in > 3)
17962306a36Sopenharmony_ci		return -EINVAL;
18062306a36Sopenharmony_ci	if (dvb->input != in) {
18162306a36Sopenharmony_ci		u32 bit = (1ULL << input->nr);
18262306a36Sopenharmony_ci		u32 obit =
18362306a36Sopenharmony_ci			dev->link[port->lnr].lnb.voltage[dvb->input & 3] & bit;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		dev->link[port->lnr].lnb.voltage[dvb->input & 3] &= ~bit;
18662306a36Sopenharmony_ci		dvb->input = in;
18762306a36Sopenharmony_ci		dev->link[port->lnr].lnb.voltage[dvb->input & 3] |= obit;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci	res = dvb->set_input(fe, in);
19062306a36Sopenharmony_ci	return res;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic int max_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct ddb_input *input = fe->sec_priv;
19662306a36Sopenharmony_ci	struct ddb_port *port = input->port;
19762306a36Sopenharmony_ci	struct ddb *dev = port->dev;
19862306a36Sopenharmony_ci	struct ddb_dvb *dvb = &port->dvb[input->nr & 1];
19962306a36Sopenharmony_ci	int tuner = 0;
20062306a36Sopenharmony_ci	int res = 0;
20162306a36Sopenharmony_ci	u32 fmode = dev->link[port->lnr].lnb.fmode;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	mutex_lock(&dev->link[port->lnr].lnb.lock);
20462306a36Sopenharmony_ci	dvb->tone = tone;
20562306a36Sopenharmony_ci	switch (fmode) {
20662306a36Sopenharmony_ci	default:
20762306a36Sopenharmony_ci	case 0:
20862306a36Sopenharmony_ci	case 3:
20962306a36Sopenharmony_ci		res = lnb_set_tone(dev, port->lnr, dvb->input, tone);
21062306a36Sopenharmony_ci		break;
21162306a36Sopenharmony_ci	case 1:
21262306a36Sopenharmony_ci	case 2:
21362306a36Sopenharmony_ci		if (old_quattro) {
21462306a36Sopenharmony_ci			if (dvb->tone == SEC_TONE_ON)
21562306a36Sopenharmony_ci				tuner |= 2;
21662306a36Sopenharmony_ci			if (dvb->voltage == SEC_VOLTAGE_18)
21762306a36Sopenharmony_ci				tuner |= 1;
21862306a36Sopenharmony_ci		} else {
21962306a36Sopenharmony_ci			if (dvb->tone == SEC_TONE_ON)
22062306a36Sopenharmony_ci				tuner |= 1;
22162306a36Sopenharmony_ci			if (dvb->voltage == SEC_VOLTAGE_18)
22262306a36Sopenharmony_ci				tuner |= 2;
22362306a36Sopenharmony_ci		}
22462306a36Sopenharmony_ci		res = max_set_input_unlocked(fe, tuner);
22562306a36Sopenharmony_ci		break;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci	mutex_unlock(&dev->link[port->lnr].lnb.lock);
22862306a36Sopenharmony_ci	return res;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic int max_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct ddb_input *input = fe->sec_priv;
23462306a36Sopenharmony_ci	struct ddb_port *port = input->port;
23562306a36Sopenharmony_ci	struct ddb *dev = port->dev;
23662306a36Sopenharmony_ci	struct ddb_dvb *dvb = &port->dvb[input->nr & 1];
23762306a36Sopenharmony_ci	int tuner = 0;
23862306a36Sopenharmony_ci	u32 nv, ov = dev->link[port->lnr].lnb.voltages;
23962306a36Sopenharmony_ci	int res = 0;
24062306a36Sopenharmony_ci	u32 fmode = dev->link[port->lnr].lnb.fmode;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	mutex_lock(&dev->link[port->lnr].lnb.lock);
24362306a36Sopenharmony_ci	dvb->voltage = voltage;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	switch (fmode) {
24662306a36Sopenharmony_ci	case 3:
24762306a36Sopenharmony_ci	default:
24862306a36Sopenharmony_ci	case 0:
24962306a36Sopenharmony_ci		if (fmode == 3)
25062306a36Sopenharmony_ci			max_set_input_unlocked(fe, 0);
25162306a36Sopenharmony_ci		if (voltage == SEC_VOLTAGE_OFF)
25262306a36Sopenharmony_ci			dev->link[port->lnr].lnb.voltage[dvb->input] &=
25362306a36Sopenharmony_ci				~(1ULL << input->nr);
25462306a36Sopenharmony_ci		else
25562306a36Sopenharmony_ci			dev->link[port->lnr].lnb.voltage[dvb->input] |=
25662306a36Sopenharmony_ci				(1ULL << input->nr);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		res = lnb_set_voltage(dev, port->lnr, dvb->input, voltage);
25962306a36Sopenharmony_ci		break;
26062306a36Sopenharmony_ci	case 1:
26162306a36Sopenharmony_ci	case 2:
26262306a36Sopenharmony_ci		if (voltage == SEC_VOLTAGE_OFF)
26362306a36Sopenharmony_ci			dev->link[port->lnr].lnb.voltages &=
26462306a36Sopenharmony_ci				~(1ULL << input->nr);
26562306a36Sopenharmony_ci		else
26662306a36Sopenharmony_ci			dev->link[port->lnr].lnb.voltages |=
26762306a36Sopenharmony_ci				(1ULL << input->nr);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		nv = dev->link[port->lnr].lnb.voltages;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		if (old_quattro) {
27262306a36Sopenharmony_ci			if (dvb->tone == SEC_TONE_ON)
27362306a36Sopenharmony_ci				tuner |= 2;
27462306a36Sopenharmony_ci			if (dvb->voltage == SEC_VOLTAGE_18)
27562306a36Sopenharmony_ci				tuner |= 1;
27662306a36Sopenharmony_ci		} else {
27762306a36Sopenharmony_ci			if (dvb->tone == SEC_TONE_ON)
27862306a36Sopenharmony_ci				tuner |= 1;
27962306a36Sopenharmony_ci			if (dvb->voltage == SEC_VOLTAGE_18)
28062306a36Sopenharmony_ci				tuner |= 2;
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci		res = max_set_input_unlocked(fe, tuner);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		if (nv != ov) {
28562306a36Sopenharmony_ci			if (nv) {
28662306a36Sopenharmony_ci				lnb_set_voltage(
28762306a36Sopenharmony_ci					dev, port->lnr,
28862306a36Sopenharmony_ci					0, SEC_VOLTAGE_13);
28962306a36Sopenharmony_ci				if (fmode == 1) {
29062306a36Sopenharmony_ci					lnb_set_voltage(
29162306a36Sopenharmony_ci						dev, port->lnr,
29262306a36Sopenharmony_ci						0, SEC_VOLTAGE_13);
29362306a36Sopenharmony_ci					if (old_quattro) {
29462306a36Sopenharmony_ci						lnb_set_voltage(
29562306a36Sopenharmony_ci							dev, port->lnr,
29662306a36Sopenharmony_ci							1, SEC_VOLTAGE_18);
29762306a36Sopenharmony_ci						lnb_set_voltage(
29862306a36Sopenharmony_ci							dev, port->lnr,
29962306a36Sopenharmony_ci							2, SEC_VOLTAGE_13);
30062306a36Sopenharmony_ci					} else {
30162306a36Sopenharmony_ci						lnb_set_voltage(
30262306a36Sopenharmony_ci							dev, port->lnr,
30362306a36Sopenharmony_ci							1, SEC_VOLTAGE_13);
30462306a36Sopenharmony_ci						lnb_set_voltage(
30562306a36Sopenharmony_ci							dev, port->lnr,
30662306a36Sopenharmony_ci							2, SEC_VOLTAGE_18);
30762306a36Sopenharmony_ci					}
30862306a36Sopenharmony_ci					lnb_set_voltage(
30962306a36Sopenharmony_ci						dev, port->lnr,
31062306a36Sopenharmony_ci						3, SEC_VOLTAGE_18);
31162306a36Sopenharmony_ci				}
31262306a36Sopenharmony_ci			} else {
31362306a36Sopenharmony_ci				lnb_set_voltage(
31462306a36Sopenharmony_ci					dev, port->lnr,
31562306a36Sopenharmony_ci					0, SEC_VOLTAGE_OFF);
31662306a36Sopenharmony_ci				if (fmode == 1) {
31762306a36Sopenharmony_ci					lnb_set_voltage(
31862306a36Sopenharmony_ci						dev, port->lnr,
31962306a36Sopenharmony_ci						1, SEC_VOLTAGE_OFF);
32062306a36Sopenharmony_ci					lnb_set_voltage(
32162306a36Sopenharmony_ci						dev, port->lnr,
32262306a36Sopenharmony_ci						2, SEC_VOLTAGE_OFF);
32362306a36Sopenharmony_ci					lnb_set_voltage(
32462306a36Sopenharmony_ci						dev, port->lnr,
32562306a36Sopenharmony_ci						3, SEC_VOLTAGE_OFF);
32662306a36Sopenharmony_ci				}
32762306a36Sopenharmony_ci			}
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci		break;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci	mutex_unlock(&dev->link[port->lnr].lnb.lock);
33262306a36Sopenharmony_ci	return res;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int max_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic int max_send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd burst)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	return 0;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic int mxl_fw_read(void *priv, u8 *buf, u32 len)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct ddb_link *link = priv;
34862306a36Sopenharmony_ci	struct ddb *dev = link->dev;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	dev_info(dev->dev, "Read mxl_fw from link %u\n", link->nr);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return ddbridge_flashread(dev, link->nr, buf, 0xc0000, len);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ciint ddb_lnb_init_fmode(struct ddb *dev, struct ddb_link *link, u32 fm)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	u32 l = link->nr;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (link->lnb.fmode == fm)
36062306a36Sopenharmony_ci		return 0;
36162306a36Sopenharmony_ci	dev_info(dev->dev, "Set fmode link %u = %u\n", l, fm);
36262306a36Sopenharmony_ci	mutex_lock(&link->lnb.lock);
36362306a36Sopenharmony_ci	if (fm == 2 || fm == 1) {
36462306a36Sopenharmony_ci		if (fmode_sat >= 0) {
36562306a36Sopenharmony_ci			lnb_set_sat(dev, l, 0, fmode_sat, 0, 0);
36662306a36Sopenharmony_ci			if (old_quattro) {
36762306a36Sopenharmony_ci				lnb_set_sat(dev, l, 1, fmode_sat, 0, 1);
36862306a36Sopenharmony_ci				lnb_set_sat(dev, l, 2, fmode_sat, 1, 0);
36962306a36Sopenharmony_ci			} else {
37062306a36Sopenharmony_ci				lnb_set_sat(dev, l, 1, fmode_sat, 1, 0);
37162306a36Sopenharmony_ci				lnb_set_sat(dev, l, 2, fmode_sat, 0, 1);
37262306a36Sopenharmony_ci			}
37362306a36Sopenharmony_ci			lnb_set_sat(dev, l, 3, fmode_sat, 1, 1);
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci		lnb_set_tone(dev, l, 0, SEC_TONE_OFF);
37662306a36Sopenharmony_ci		if (old_quattro) {
37762306a36Sopenharmony_ci			lnb_set_tone(dev, l, 1, SEC_TONE_OFF);
37862306a36Sopenharmony_ci			lnb_set_tone(dev, l, 2, SEC_TONE_ON);
37962306a36Sopenharmony_ci		} else {
38062306a36Sopenharmony_ci			lnb_set_tone(dev, l, 1, SEC_TONE_ON);
38162306a36Sopenharmony_ci			lnb_set_tone(dev, l, 2, SEC_TONE_OFF);
38262306a36Sopenharmony_ci		}
38362306a36Sopenharmony_ci		lnb_set_tone(dev, l, 3, SEC_TONE_ON);
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	link->lnb.fmode = fm;
38662306a36Sopenharmony_ci	mutex_unlock(&link->lnb.lock);
38762306a36Sopenharmony_ci	return 0;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic struct mxl5xx_cfg mxl5xx = {
39162306a36Sopenharmony_ci	.adr      = 0x60,
39262306a36Sopenharmony_ci	.type     = 0x01,
39362306a36Sopenharmony_ci	.clk      = 27000000,
39462306a36Sopenharmony_ci	.ts_clk   = 139,
39562306a36Sopenharmony_ci	.cap      = 12,
39662306a36Sopenharmony_ci	.fw_read  = mxl_fw_read,
39762306a36Sopenharmony_ci};
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ciint ddb_fe_attach_mxl5xx(struct ddb_input *input)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct ddb *dev = input->port->dev;
40262306a36Sopenharmony_ci	struct i2c_adapter *i2c = &input->port->i2c->adap;
40362306a36Sopenharmony_ci	struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1];
40462306a36Sopenharmony_ci	struct ddb_port *port = input->port;
40562306a36Sopenharmony_ci	struct ddb_link *link = &dev->link[port->lnr];
40662306a36Sopenharmony_ci	struct mxl5xx_cfg cfg;
40762306a36Sopenharmony_ci	int demod, tuner;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	cfg = mxl5xx;
41062306a36Sopenharmony_ci	cfg.fw_priv = link;
41162306a36Sopenharmony_ci	dvb->set_input = NULL;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	demod = input->nr;
41462306a36Sopenharmony_ci	tuner = demod & 3;
41562306a36Sopenharmony_ci	if (fmode == 3)
41662306a36Sopenharmony_ci		tuner = 0;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	dvb->fe = dvb_attach(mxl5xx_attach, i2c, &cfg,
41962306a36Sopenharmony_ci			     demod, tuner, &dvb->set_input);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (!dvb->fe) {
42262306a36Sopenharmony_ci		dev_err(dev->dev, "No MXL5XX found!\n");
42362306a36Sopenharmony_ci		return -ENODEV;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (!dvb->set_input) {
42762306a36Sopenharmony_ci		dev_err(dev->dev, "No mxl5xx_set_input function pointer!\n");
42862306a36Sopenharmony_ci		return -ENODEV;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (input->nr < 4) {
43262306a36Sopenharmony_ci		lnb_command(dev, port->lnr, input->nr, LNB_CMD_INIT);
43362306a36Sopenharmony_ci		lnb_set_voltage(dev, port->lnr, input->nr, SEC_VOLTAGE_OFF);
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci	ddb_lnb_init_fmode(dev, link, fmode);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	dvb->fe->ops.set_voltage = max_set_voltage;
43862306a36Sopenharmony_ci	dvb->fe->ops.enable_high_lnb_voltage = max_enable_high_lnb_voltage;
43962306a36Sopenharmony_ci	dvb->fe->ops.set_tone = max_set_tone;
44062306a36Sopenharmony_ci	dvb->diseqc_send_master_cmd = dvb->fe->ops.diseqc_send_master_cmd;
44162306a36Sopenharmony_ci	dvb->fe->ops.diseqc_send_master_cmd = max_send_master_cmd;
44262306a36Sopenharmony_ci	dvb->fe->ops.diseqc_send_burst = max_send_burst;
44362306a36Sopenharmony_ci	dvb->fe->sec_priv = input;
44462306a36Sopenharmony_ci	dvb->input = tuner;
44562306a36Sopenharmony_ci	return 0;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/******************************************************************************/
44962306a36Sopenharmony_ci/* MAX MCI related functions */
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ciint ddb_fe_attach_mci(struct ddb_input *input, u32 type)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct ddb *dev = input->port->dev;
45462306a36Sopenharmony_ci	struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1];
45562306a36Sopenharmony_ci	struct ddb_port *port = input->port;
45662306a36Sopenharmony_ci	struct ddb_link *link = &dev->link[port->lnr];
45762306a36Sopenharmony_ci	int demod, tuner;
45862306a36Sopenharmony_ci	struct mci_cfg cfg;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	demod = input->nr;
46162306a36Sopenharmony_ci	tuner = demod & 3;
46262306a36Sopenharmony_ci	switch (type) {
46362306a36Sopenharmony_ci	case DDB_TUNER_MCI_SX8:
46462306a36Sopenharmony_ci		cfg = ddb_max_sx8_cfg;
46562306a36Sopenharmony_ci		if (fmode == 3)
46662306a36Sopenharmony_ci			tuner = 0;
46762306a36Sopenharmony_ci		break;
46862306a36Sopenharmony_ci	default:
46962306a36Sopenharmony_ci		return -EINVAL;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci	dvb->fe = ddb_mci_attach(input, &cfg, demod, &dvb->set_input);
47262306a36Sopenharmony_ci	if (!dvb->fe) {
47362306a36Sopenharmony_ci		dev_err(dev->dev, "No MCI card found!\n");
47462306a36Sopenharmony_ci		return -ENODEV;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci	if (!dvb->set_input) {
47762306a36Sopenharmony_ci		dev_err(dev->dev, "No MCI set_input function pointer!\n");
47862306a36Sopenharmony_ci		return -ENODEV;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci	if (input->nr < 4) {
48162306a36Sopenharmony_ci		lnb_command(dev, port->lnr, input->nr, LNB_CMD_INIT);
48262306a36Sopenharmony_ci		lnb_set_voltage(dev, port->lnr, input->nr, SEC_VOLTAGE_OFF);
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci	ddb_lnb_init_fmode(dev, link, fmode);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	dvb->fe->ops.set_voltage = max_set_voltage;
48762306a36Sopenharmony_ci	dvb->fe->ops.enable_high_lnb_voltage = max_enable_high_lnb_voltage;
48862306a36Sopenharmony_ci	dvb->fe->ops.set_tone = max_set_tone;
48962306a36Sopenharmony_ci	dvb->diseqc_send_master_cmd = dvb->fe->ops.diseqc_send_master_cmd;
49062306a36Sopenharmony_ci	dvb->fe->ops.diseqc_send_master_cmd = max_send_master_cmd;
49162306a36Sopenharmony_ci	dvb->fe->ops.diseqc_send_burst = max_send_burst;
49262306a36Sopenharmony_ci	dvb->fe->sec_priv = input;
49362306a36Sopenharmony_ci	dvb->input = tuner;
49462306a36Sopenharmony_ci	return 0;
49562306a36Sopenharmony_ci}
496