18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ddbridge-max.c: Digital Devices bridge MAX card support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010-2017 Digital Devices GmbH
68c2ecf20Sopenharmony_ci *                         Ralph Metzler <rjkm@metzlerbros.de>
78c2ecf20Sopenharmony_ci *                         Marcus Metzler <mocm@metzlerbros.de>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
108c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License
118c2ecf20Sopenharmony_ci * version 2 only, as published by the Free Software Foundation.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful,
148c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
168c2ecf20Sopenharmony_ci * GNU General Public License for more details.
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/init.h>
218c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
228c2ecf20Sopenharmony_ci#include <linux/delay.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci#include <linux/poll.h>
258c2ecf20Sopenharmony_ci#include <linux/io.h>
268c2ecf20Sopenharmony_ci#include <linux/pci.h>
278c2ecf20Sopenharmony_ci#include <linux/pci_ids.h>
288c2ecf20Sopenharmony_ci#include <linux/timer.h>
298c2ecf20Sopenharmony_ci#include <linux/i2c.h>
308c2ecf20Sopenharmony_ci#include <linux/swab.h>
318c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "ddbridge.h"
348c2ecf20Sopenharmony_ci#include "ddbridge-regs.h"
358c2ecf20Sopenharmony_ci#include "ddbridge-io.h"
368c2ecf20Sopenharmony_ci#include "ddbridge-mci.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#include "ddbridge-max.h"
398c2ecf20Sopenharmony_ci#include "mxl5xx.h"
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/******************************************************************************/
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* MaxS4/8 related modparams */
448c2ecf20Sopenharmony_cistatic int fmode;
458c2ecf20Sopenharmony_cimodule_param(fmode, int, 0444);
468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fmode, "frontend emulation mode");
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int fmode_sat = -1;
498c2ecf20Sopenharmony_cimodule_param(fmode_sat, int, 0444);
508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fmode_sat, "set frontend emulation mode sat");
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic int old_quattro;
538c2ecf20Sopenharmony_cimodule_param(old_quattro, int, 0444);
548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(old_quattro, "old quattro LNB input order ");
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/******************************************************************************/
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int lnb_command(struct ddb *dev, u32 link, u32 lnb, u32 cmd)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	u32 c, v = 0, tag = DDB_LINK_TAG(link);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	v = LNB_TONE & (dev->link[link].lnb.tone << (15 - lnb));
638c2ecf20Sopenharmony_ci	ddbwritel(dev, cmd | v, tag | LNB_CONTROL(lnb));
648c2ecf20Sopenharmony_ci	for (c = 0; c < 10; c++) {
658c2ecf20Sopenharmony_ci		v = ddbreadl(dev, tag | LNB_CONTROL(lnb));
668c2ecf20Sopenharmony_ci		if ((v & LNB_BUSY) == 0)
678c2ecf20Sopenharmony_ci			break;
688c2ecf20Sopenharmony_ci		msleep(20);
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	if (c == 10)
718c2ecf20Sopenharmony_ci		dev_info(dev->dev, "%s lnb = %08x  cmd = %08x\n",
728c2ecf20Sopenharmony_ci			 __func__, lnb, cmd);
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int max_send_master_cmd(struct dvb_frontend *fe,
778c2ecf20Sopenharmony_ci			       struct dvb_diseqc_master_cmd *cmd)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct ddb_input *input = fe->sec_priv;
808c2ecf20Sopenharmony_ci	struct ddb_port *port = input->port;
818c2ecf20Sopenharmony_ci	struct ddb *dev = port->dev;
828c2ecf20Sopenharmony_ci	struct ddb_dvb *dvb = &port->dvb[input->nr & 1];
838c2ecf20Sopenharmony_ci	u32 tag = DDB_LINK_TAG(port->lnr);
848c2ecf20Sopenharmony_ci	int i;
858c2ecf20Sopenharmony_ci	u32 fmode = dev->link[port->lnr].lnb.fmode;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (fmode == 2 || fmode == 1)
888c2ecf20Sopenharmony_ci		return 0;
898c2ecf20Sopenharmony_ci	if (dvb->diseqc_send_master_cmd)
908c2ecf20Sopenharmony_ci		dvb->diseqc_send_master_cmd(fe, cmd);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	mutex_lock(&dev->link[port->lnr].lnb.lock);
938c2ecf20Sopenharmony_ci	ddbwritel(dev, 0, tag | LNB_BUF_LEVEL(dvb->input));
948c2ecf20Sopenharmony_ci	for (i = 0; i < cmd->msg_len; i++)
958c2ecf20Sopenharmony_ci		ddbwritel(dev, cmd->msg[i], tag | LNB_BUF_WRITE(dvb->input));
968c2ecf20Sopenharmony_ci	lnb_command(dev, port->lnr, dvb->input, LNB_CMD_DISEQC);
978c2ecf20Sopenharmony_ci	mutex_unlock(&dev->link[port->lnr].lnb.lock);
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int lnb_send_diseqc(struct ddb *dev, u32 link, u32 input,
1028c2ecf20Sopenharmony_ci			   struct dvb_diseqc_master_cmd *cmd)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	u32 tag = DDB_LINK_TAG(link);
1058c2ecf20Sopenharmony_ci	int i;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	ddbwritel(dev, 0, tag | LNB_BUF_LEVEL(input));
1088c2ecf20Sopenharmony_ci	for (i = 0; i < cmd->msg_len; i++)
1098c2ecf20Sopenharmony_ci		ddbwritel(dev, cmd->msg[i], tag | LNB_BUF_WRITE(input));
1108c2ecf20Sopenharmony_ci	lnb_command(dev, link, input, LNB_CMD_DISEQC);
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int lnb_set_sat(struct ddb *dev, u32 link, u32 input, u32 sat, u32 band,
1158c2ecf20Sopenharmony_ci		       u32 hor)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct dvb_diseqc_master_cmd cmd = {
1188c2ecf20Sopenharmony_ci		.msg = {0xe0, 0x10, 0x38, 0xf0, 0x00, 0x00},
1198c2ecf20Sopenharmony_ci		.msg_len = 4
1208c2ecf20Sopenharmony_ci	};
1218c2ecf20Sopenharmony_ci	cmd.msg[3] = 0xf0 | (((sat << 2) & 0x0c) | (band ? 1 : 0) |
1228c2ecf20Sopenharmony_ci		(hor ? 2 : 0));
1238c2ecf20Sopenharmony_ci	return lnb_send_diseqc(dev, link, input, &cmd);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int lnb_set_tone(struct ddb *dev, u32 link, u32 input,
1278c2ecf20Sopenharmony_ci			enum fe_sec_tone_mode tone)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int s = 0;
1308c2ecf20Sopenharmony_ci	u32 mask = (1ULL << input);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	switch (tone) {
1338c2ecf20Sopenharmony_ci	case SEC_TONE_OFF:
1348c2ecf20Sopenharmony_ci		if (!(dev->link[link].lnb.tone & mask))
1358c2ecf20Sopenharmony_ci			return 0;
1368c2ecf20Sopenharmony_ci		dev->link[link].lnb.tone &= ~(1ULL << input);
1378c2ecf20Sopenharmony_ci		break;
1388c2ecf20Sopenharmony_ci	case SEC_TONE_ON:
1398c2ecf20Sopenharmony_ci		if (dev->link[link].lnb.tone & mask)
1408c2ecf20Sopenharmony_ci			return 0;
1418c2ecf20Sopenharmony_ci		dev->link[link].lnb.tone |= (1ULL << input);
1428c2ecf20Sopenharmony_ci		break;
1438c2ecf20Sopenharmony_ci	default:
1448c2ecf20Sopenharmony_ci		s = -EINVAL;
1458c2ecf20Sopenharmony_ci		break;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci	if (!s)
1488c2ecf20Sopenharmony_ci		s = lnb_command(dev, link, input, LNB_CMD_NOP);
1498c2ecf20Sopenharmony_ci	return s;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic int lnb_set_voltage(struct ddb *dev, u32 link, u32 input,
1538c2ecf20Sopenharmony_ci			   enum fe_sec_voltage voltage)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	int s = 0;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (dev->link[link].lnb.oldvoltage[input] == voltage)
1588c2ecf20Sopenharmony_ci		return 0;
1598c2ecf20Sopenharmony_ci	switch (voltage) {
1608c2ecf20Sopenharmony_ci	case SEC_VOLTAGE_OFF:
1618c2ecf20Sopenharmony_ci		if (dev->link[link].lnb.voltage[input])
1628c2ecf20Sopenharmony_ci			return 0;
1638c2ecf20Sopenharmony_ci		lnb_command(dev, link, input, LNB_CMD_OFF);
1648c2ecf20Sopenharmony_ci		break;
1658c2ecf20Sopenharmony_ci	case SEC_VOLTAGE_13:
1668c2ecf20Sopenharmony_ci		lnb_command(dev, link, input, LNB_CMD_LOW);
1678c2ecf20Sopenharmony_ci		break;
1688c2ecf20Sopenharmony_ci	case SEC_VOLTAGE_18:
1698c2ecf20Sopenharmony_ci		lnb_command(dev, link, input, LNB_CMD_HIGH);
1708c2ecf20Sopenharmony_ci		break;
1718c2ecf20Sopenharmony_ci	default:
1728c2ecf20Sopenharmony_ci		s = -EINVAL;
1738c2ecf20Sopenharmony_ci		break;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci	dev->link[link].lnb.oldvoltage[input] = voltage;
1768c2ecf20Sopenharmony_ci	return s;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic int max_set_input_unlocked(struct dvb_frontend *fe, int in)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct ddb_input *input = fe->sec_priv;
1828c2ecf20Sopenharmony_ci	struct ddb_port *port = input->port;
1838c2ecf20Sopenharmony_ci	struct ddb *dev = port->dev;
1848c2ecf20Sopenharmony_ci	struct ddb_dvb *dvb = &port->dvb[input->nr & 1];
1858c2ecf20Sopenharmony_ci	int res = 0;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (in > 3)
1888c2ecf20Sopenharmony_ci		return -EINVAL;
1898c2ecf20Sopenharmony_ci	if (dvb->input != in) {
1908c2ecf20Sopenharmony_ci		u32 bit = (1ULL << input->nr);
1918c2ecf20Sopenharmony_ci		u32 obit =
1928c2ecf20Sopenharmony_ci			dev->link[port->lnr].lnb.voltage[dvb->input & 3] & bit;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		dev->link[port->lnr].lnb.voltage[dvb->input & 3] &= ~bit;
1958c2ecf20Sopenharmony_ci		dvb->input = in;
1968c2ecf20Sopenharmony_ci		dev->link[port->lnr].lnb.voltage[dvb->input & 3] |= obit;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ci	res = dvb->set_input(fe, in);
1998c2ecf20Sopenharmony_ci	return res;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int max_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct ddb_input *input = fe->sec_priv;
2058c2ecf20Sopenharmony_ci	struct ddb_port *port = input->port;
2068c2ecf20Sopenharmony_ci	struct ddb *dev = port->dev;
2078c2ecf20Sopenharmony_ci	struct ddb_dvb *dvb = &port->dvb[input->nr & 1];
2088c2ecf20Sopenharmony_ci	int tuner = 0;
2098c2ecf20Sopenharmony_ci	int res = 0;
2108c2ecf20Sopenharmony_ci	u32 fmode = dev->link[port->lnr].lnb.fmode;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	mutex_lock(&dev->link[port->lnr].lnb.lock);
2138c2ecf20Sopenharmony_ci	dvb->tone = tone;
2148c2ecf20Sopenharmony_ci	switch (fmode) {
2158c2ecf20Sopenharmony_ci	default:
2168c2ecf20Sopenharmony_ci	case 0:
2178c2ecf20Sopenharmony_ci	case 3:
2188c2ecf20Sopenharmony_ci		res = lnb_set_tone(dev, port->lnr, dvb->input, tone);
2198c2ecf20Sopenharmony_ci		break;
2208c2ecf20Sopenharmony_ci	case 1:
2218c2ecf20Sopenharmony_ci	case 2:
2228c2ecf20Sopenharmony_ci		if (old_quattro) {
2238c2ecf20Sopenharmony_ci			if (dvb->tone == SEC_TONE_ON)
2248c2ecf20Sopenharmony_ci				tuner |= 2;
2258c2ecf20Sopenharmony_ci			if (dvb->voltage == SEC_VOLTAGE_18)
2268c2ecf20Sopenharmony_ci				tuner |= 1;
2278c2ecf20Sopenharmony_ci		} else {
2288c2ecf20Sopenharmony_ci			if (dvb->tone == SEC_TONE_ON)
2298c2ecf20Sopenharmony_ci				tuner |= 1;
2308c2ecf20Sopenharmony_ci			if (dvb->voltage == SEC_VOLTAGE_18)
2318c2ecf20Sopenharmony_ci				tuner |= 2;
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci		res = max_set_input_unlocked(fe, tuner);
2348c2ecf20Sopenharmony_ci		break;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci	mutex_unlock(&dev->link[port->lnr].lnb.lock);
2378c2ecf20Sopenharmony_ci	return res;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic int max_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage voltage)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct ddb_input *input = fe->sec_priv;
2438c2ecf20Sopenharmony_ci	struct ddb_port *port = input->port;
2448c2ecf20Sopenharmony_ci	struct ddb *dev = port->dev;
2458c2ecf20Sopenharmony_ci	struct ddb_dvb *dvb = &port->dvb[input->nr & 1];
2468c2ecf20Sopenharmony_ci	int tuner = 0;
2478c2ecf20Sopenharmony_ci	u32 nv, ov = dev->link[port->lnr].lnb.voltages;
2488c2ecf20Sopenharmony_ci	int res = 0;
2498c2ecf20Sopenharmony_ci	u32 fmode = dev->link[port->lnr].lnb.fmode;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	mutex_lock(&dev->link[port->lnr].lnb.lock);
2528c2ecf20Sopenharmony_ci	dvb->voltage = voltage;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	switch (fmode) {
2558c2ecf20Sopenharmony_ci	case 3:
2568c2ecf20Sopenharmony_ci	default:
2578c2ecf20Sopenharmony_ci	case 0:
2588c2ecf20Sopenharmony_ci		if (fmode == 3)
2598c2ecf20Sopenharmony_ci			max_set_input_unlocked(fe, 0);
2608c2ecf20Sopenharmony_ci		if (voltage == SEC_VOLTAGE_OFF)
2618c2ecf20Sopenharmony_ci			dev->link[port->lnr].lnb.voltage[dvb->input] &=
2628c2ecf20Sopenharmony_ci				~(1ULL << input->nr);
2638c2ecf20Sopenharmony_ci		else
2648c2ecf20Sopenharmony_ci			dev->link[port->lnr].lnb.voltage[dvb->input] |=
2658c2ecf20Sopenharmony_ci				(1ULL << input->nr);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		res = lnb_set_voltage(dev, port->lnr, dvb->input, voltage);
2688c2ecf20Sopenharmony_ci		break;
2698c2ecf20Sopenharmony_ci	case 1:
2708c2ecf20Sopenharmony_ci	case 2:
2718c2ecf20Sopenharmony_ci		if (voltage == SEC_VOLTAGE_OFF)
2728c2ecf20Sopenharmony_ci			dev->link[port->lnr].lnb.voltages &=
2738c2ecf20Sopenharmony_ci				~(1ULL << input->nr);
2748c2ecf20Sopenharmony_ci		else
2758c2ecf20Sopenharmony_ci			dev->link[port->lnr].lnb.voltages |=
2768c2ecf20Sopenharmony_ci				(1ULL << input->nr);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		nv = dev->link[port->lnr].lnb.voltages;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		if (old_quattro) {
2818c2ecf20Sopenharmony_ci			if (dvb->tone == SEC_TONE_ON)
2828c2ecf20Sopenharmony_ci				tuner |= 2;
2838c2ecf20Sopenharmony_ci			if (dvb->voltage == SEC_VOLTAGE_18)
2848c2ecf20Sopenharmony_ci				tuner |= 1;
2858c2ecf20Sopenharmony_ci		} else {
2868c2ecf20Sopenharmony_ci			if (dvb->tone == SEC_TONE_ON)
2878c2ecf20Sopenharmony_ci				tuner |= 1;
2888c2ecf20Sopenharmony_ci			if (dvb->voltage == SEC_VOLTAGE_18)
2898c2ecf20Sopenharmony_ci				tuner |= 2;
2908c2ecf20Sopenharmony_ci		}
2918c2ecf20Sopenharmony_ci		res = max_set_input_unlocked(fe, tuner);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		if (nv != ov) {
2948c2ecf20Sopenharmony_ci			if (nv) {
2958c2ecf20Sopenharmony_ci				lnb_set_voltage(
2968c2ecf20Sopenharmony_ci					dev, port->lnr,
2978c2ecf20Sopenharmony_ci					0, SEC_VOLTAGE_13);
2988c2ecf20Sopenharmony_ci				if (fmode == 1) {
2998c2ecf20Sopenharmony_ci					lnb_set_voltage(
3008c2ecf20Sopenharmony_ci						dev, port->lnr,
3018c2ecf20Sopenharmony_ci						0, SEC_VOLTAGE_13);
3028c2ecf20Sopenharmony_ci					if (old_quattro) {
3038c2ecf20Sopenharmony_ci						lnb_set_voltage(
3048c2ecf20Sopenharmony_ci							dev, port->lnr,
3058c2ecf20Sopenharmony_ci							1, SEC_VOLTAGE_18);
3068c2ecf20Sopenharmony_ci						lnb_set_voltage(
3078c2ecf20Sopenharmony_ci							dev, port->lnr,
3088c2ecf20Sopenharmony_ci							2, SEC_VOLTAGE_13);
3098c2ecf20Sopenharmony_ci					} else {
3108c2ecf20Sopenharmony_ci						lnb_set_voltage(
3118c2ecf20Sopenharmony_ci							dev, port->lnr,
3128c2ecf20Sopenharmony_ci							1, SEC_VOLTAGE_13);
3138c2ecf20Sopenharmony_ci						lnb_set_voltage(
3148c2ecf20Sopenharmony_ci							dev, port->lnr,
3158c2ecf20Sopenharmony_ci							2, SEC_VOLTAGE_18);
3168c2ecf20Sopenharmony_ci					}
3178c2ecf20Sopenharmony_ci					lnb_set_voltage(
3188c2ecf20Sopenharmony_ci						dev, port->lnr,
3198c2ecf20Sopenharmony_ci						3, SEC_VOLTAGE_18);
3208c2ecf20Sopenharmony_ci				}
3218c2ecf20Sopenharmony_ci			} else {
3228c2ecf20Sopenharmony_ci				lnb_set_voltage(
3238c2ecf20Sopenharmony_ci					dev, port->lnr,
3248c2ecf20Sopenharmony_ci					0, SEC_VOLTAGE_OFF);
3258c2ecf20Sopenharmony_ci				if (fmode == 1) {
3268c2ecf20Sopenharmony_ci					lnb_set_voltage(
3278c2ecf20Sopenharmony_ci						dev, port->lnr,
3288c2ecf20Sopenharmony_ci						1, SEC_VOLTAGE_OFF);
3298c2ecf20Sopenharmony_ci					lnb_set_voltage(
3308c2ecf20Sopenharmony_ci						dev, port->lnr,
3318c2ecf20Sopenharmony_ci						2, SEC_VOLTAGE_OFF);
3328c2ecf20Sopenharmony_ci					lnb_set_voltage(
3338c2ecf20Sopenharmony_ci						dev, port->lnr,
3348c2ecf20Sopenharmony_ci						3, SEC_VOLTAGE_OFF);
3358c2ecf20Sopenharmony_ci				}
3368c2ecf20Sopenharmony_ci			}
3378c2ecf20Sopenharmony_ci		}
3388c2ecf20Sopenharmony_ci		break;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci	mutex_unlock(&dev->link[port->lnr].lnb.lock);
3418c2ecf20Sopenharmony_ci	return res;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int max_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int max_send_burst(struct dvb_frontend *fe, enum fe_sec_mini_cmd burst)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	return 0;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int mxl_fw_read(void *priv, u8 *buf, u32 len)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct ddb_link *link = priv;
3578c2ecf20Sopenharmony_ci	struct ddb *dev = link->dev;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	dev_info(dev->dev, "Read mxl_fw from link %u\n", link->nr);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	return ddbridge_flashread(dev, link->nr, buf, 0xc0000, len);
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ciint ddb_lnb_init_fmode(struct ddb *dev, struct ddb_link *link, u32 fm)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	u32 l = link->nr;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (link->lnb.fmode == fm)
3698c2ecf20Sopenharmony_ci		return 0;
3708c2ecf20Sopenharmony_ci	dev_info(dev->dev, "Set fmode link %u = %u\n", l, fm);
3718c2ecf20Sopenharmony_ci	mutex_lock(&link->lnb.lock);
3728c2ecf20Sopenharmony_ci	if (fm == 2 || fm == 1) {
3738c2ecf20Sopenharmony_ci		if (fmode_sat >= 0) {
3748c2ecf20Sopenharmony_ci			lnb_set_sat(dev, l, 0, fmode_sat, 0, 0);
3758c2ecf20Sopenharmony_ci			if (old_quattro) {
3768c2ecf20Sopenharmony_ci				lnb_set_sat(dev, l, 1, fmode_sat, 0, 1);
3778c2ecf20Sopenharmony_ci				lnb_set_sat(dev, l, 2, fmode_sat, 1, 0);
3788c2ecf20Sopenharmony_ci			} else {
3798c2ecf20Sopenharmony_ci				lnb_set_sat(dev, l, 1, fmode_sat, 1, 0);
3808c2ecf20Sopenharmony_ci				lnb_set_sat(dev, l, 2, fmode_sat, 0, 1);
3818c2ecf20Sopenharmony_ci			}
3828c2ecf20Sopenharmony_ci			lnb_set_sat(dev, l, 3, fmode_sat, 1, 1);
3838c2ecf20Sopenharmony_ci		}
3848c2ecf20Sopenharmony_ci		lnb_set_tone(dev, l, 0, SEC_TONE_OFF);
3858c2ecf20Sopenharmony_ci		if (old_quattro) {
3868c2ecf20Sopenharmony_ci			lnb_set_tone(dev, l, 1, SEC_TONE_OFF);
3878c2ecf20Sopenharmony_ci			lnb_set_tone(dev, l, 2, SEC_TONE_ON);
3888c2ecf20Sopenharmony_ci		} else {
3898c2ecf20Sopenharmony_ci			lnb_set_tone(dev, l, 1, SEC_TONE_ON);
3908c2ecf20Sopenharmony_ci			lnb_set_tone(dev, l, 2, SEC_TONE_OFF);
3918c2ecf20Sopenharmony_ci		}
3928c2ecf20Sopenharmony_ci		lnb_set_tone(dev, l, 3, SEC_TONE_ON);
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci	link->lnb.fmode = fm;
3958c2ecf20Sopenharmony_ci	mutex_unlock(&link->lnb.lock);
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic struct mxl5xx_cfg mxl5xx = {
4008c2ecf20Sopenharmony_ci	.adr      = 0x60,
4018c2ecf20Sopenharmony_ci	.type     = 0x01,
4028c2ecf20Sopenharmony_ci	.clk      = 27000000,
4038c2ecf20Sopenharmony_ci	.ts_clk   = 139,
4048c2ecf20Sopenharmony_ci	.cap      = 12,
4058c2ecf20Sopenharmony_ci	.fw_read  = mxl_fw_read,
4068c2ecf20Sopenharmony_ci};
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ciint ddb_fe_attach_mxl5xx(struct ddb_input *input)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct ddb *dev = input->port->dev;
4118c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = &input->port->i2c->adap;
4128c2ecf20Sopenharmony_ci	struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1];
4138c2ecf20Sopenharmony_ci	struct ddb_port *port = input->port;
4148c2ecf20Sopenharmony_ci	struct ddb_link *link = &dev->link[port->lnr];
4158c2ecf20Sopenharmony_ci	struct mxl5xx_cfg cfg;
4168c2ecf20Sopenharmony_ci	int demod, tuner;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	cfg = mxl5xx;
4198c2ecf20Sopenharmony_ci	cfg.fw_priv = link;
4208c2ecf20Sopenharmony_ci	dvb->set_input = NULL;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	demod = input->nr;
4238c2ecf20Sopenharmony_ci	tuner = demod & 3;
4248c2ecf20Sopenharmony_ci	if (fmode == 3)
4258c2ecf20Sopenharmony_ci		tuner = 0;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	dvb->fe = dvb_attach(mxl5xx_attach, i2c, &cfg,
4288c2ecf20Sopenharmony_ci			     demod, tuner, &dvb->set_input);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (!dvb->fe) {
4318c2ecf20Sopenharmony_ci		dev_err(dev->dev, "No MXL5XX found!\n");
4328c2ecf20Sopenharmony_ci		return -ENODEV;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (!dvb->set_input) {
4368c2ecf20Sopenharmony_ci		dev_err(dev->dev, "No mxl5xx_set_input function pointer!\n");
4378c2ecf20Sopenharmony_ci		return -ENODEV;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (input->nr < 4) {
4418c2ecf20Sopenharmony_ci		lnb_command(dev, port->lnr, input->nr, LNB_CMD_INIT);
4428c2ecf20Sopenharmony_ci		lnb_set_voltage(dev, port->lnr, input->nr, SEC_VOLTAGE_OFF);
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci	ddb_lnb_init_fmode(dev, link, fmode);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	dvb->fe->ops.set_voltage = max_set_voltage;
4478c2ecf20Sopenharmony_ci	dvb->fe->ops.enable_high_lnb_voltage = max_enable_high_lnb_voltage;
4488c2ecf20Sopenharmony_ci	dvb->fe->ops.set_tone = max_set_tone;
4498c2ecf20Sopenharmony_ci	dvb->diseqc_send_master_cmd = dvb->fe->ops.diseqc_send_master_cmd;
4508c2ecf20Sopenharmony_ci	dvb->fe->ops.diseqc_send_master_cmd = max_send_master_cmd;
4518c2ecf20Sopenharmony_ci	dvb->fe->ops.diseqc_send_burst = max_send_burst;
4528c2ecf20Sopenharmony_ci	dvb->fe->sec_priv = input;
4538c2ecf20Sopenharmony_ci	dvb->input = tuner;
4548c2ecf20Sopenharmony_ci	return 0;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci/******************************************************************************/
4588c2ecf20Sopenharmony_ci/* MAX MCI related functions */
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ciint ddb_fe_attach_mci(struct ddb_input *input, u32 type)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	struct ddb *dev = input->port->dev;
4638c2ecf20Sopenharmony_ci	struct ddb_dvb *dvb = &input->port->dvb[input->nr & 1];
4648c2ecf20Sopenharmony_ci	struct ddb_port *port = input->port;
4658c2ecf20Sopenharmony_ci	struct ddb_link *link = &dev->link[port->lnr];
4668c2ecf20Sopenharmony_ci	int demod, tuner;
4678c2ecf20Sopenharmony_ci	struct mci_cfg cfg;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	demod = input->nr;
4708c2ecf20Sopenharmony_ci	tuner = demod & 3;
4718c2ecf20Sopenharmony_ci	switch (type) {
4728c2ecf20Sopenharmony_ci	case DDB_TUNER_MCI_SX8:
4738c2ecf20Sopenharmony_ci		cfg = ddb_max_sx8_cfg;
4748c2ecf20Sopenharmony_ci		if (fmode == 3)
4758c2ecf20Sopenharmony_ci			tuner = 0;
4768c2ecf20Sopenharmony_ci		break;
4778c2ecf20Sopenharmony_ci	default:
4788c2ecf20Sopenharmony_ci		return -EINVAL;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci	dvb->fe = ddb_mci_attach(input, &cfg, demod, &dvb->set_input);
4818c2ecf20Sopenharmony_ci	if (!dvb->fe) {
4828c2ecf20Sopenharmony_ci		dev_err(dev->dev, "No MCI card found!\n");
4838c2ecf20Sopenharmony_ci		return -ENODEV;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci	if (!dvb->set_input) {
4868c2ecf20Sopenharmony_ci		dev_err(dev->dev, "No MCI set_input function pointer!\n");
4878c2ecf20Sopenharmony_ci		return -ENODEV;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci	if (input->nr < 4) {
4908c2ecf20Sopenharmony_ci		lnb_command(dev, port->lnr, input->nr, LNB_CMD_INIT);
4918c2ecf20Sopenharmony_ci		lnb_set_voltage(dev, port->lnr, input->nr, SEC_VOLTAGE_OFF);
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci	ddb_lnb_init_fmode(dev, link, fmode);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	dvb->fe->ops.set_voltage = max_set_voltage;
4968c2ecf20Sopenharmony_ci	dvb->fe->ops.enable_high_lnb_voltage = max_enable_high_lnb_voltage;
4978c2ecf20Sopenharmony_ci	dvb->fe->ops.set_tone = max_set_tone;
4988c2ecf20Sopenharmony_ci	dvb->diseqc_send_master_cmd = dvb->fe->ops.diseqc_send_master_cmd;
4998c2ecf20Sopenharmony_ci	dvb->fe->ops.diseqc_send_master_cmd = max_send_master_cmd;
5008c2ecf20Sopenharmony_ci	dvb->fe->ops.diseqc_send_burst = max_send_burst;
5018c2ecf20Sopenharmony_ci	dvb->fe->sec_priv = input;
5028c2ecf20Sopenharmony_ci	dvb->input = tuner;
5038c2ecf20Sopenharmony_ci	return 0;
5048c2ecf20Sopenharmony_ci}
505