18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ngene-cards.c: nGene PCIe bridge driver - card specific info
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2005-2007 Micronas
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
88c2ecf20Sopenharmony_ci *                         Modifications for new nGene firmware,
98c2ecf20Sopenharmony_ci *                         support for EEPROM-copying,
108c2ecf20Sopenharmony_ci *                         support for new dual DVB-S2 card prototype
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/pci.h>
188c2ecf20Sopenharmony_ci#include <linux/pci_ids.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "ngene.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* demods/tuners */
238c2ecf20Sopenharmony_ci#include "stv6110x.h"
248c2ecf20Sopenharmony_ci#include "stv090x.h"
258c2ecf20Sopenharmony_ci#include "lnbh24.h"
268c2ecf20Sopenharmony_ci#include "lgdt330x.h"
278c2ecf20Sopenharmony_ci#include "mt2131.h"
288c2ecf20Sopenharmony_ci#include "tda18271c2dd.h"
298c2ecf20Sopenharmony_ci#include "drxk.h"
308c2ecf20Sopenharmony_ci#include "drxd.h"
318c2ecf20Sopenharmony_ci#include "dvb-pll.h"
328c2ecf20Sopenharmony_ci#include "stv0367.h"
338c2ecf20Sopenharmony_ci#include "stv0367_priv.h"
348c2ecf20Sopenharmony_ci#include "tda18212.h"
358c2ecf20Sopenharmony_ci#include "cxd2841er.h"
368c2ecf20Sopenharmony_ci#include "stv0910.h"
378c2ecf20Sopenharmony_ci#include "stv6111.h"
388c2ecf20Sopenharmony_ci#include "lnbh25.h"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/****************************************************************************/
418c2ecf20Sopenharmony_ci/* I2C transfer functions used for demod/tuner probing***********************/
428c2ecf20Sopenharmony_ci/****************************************************************************/
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int i2c_io(struct i2c_adapter *adapter, u8 adr,
458c2ecf20Sopenharmony_ci		  u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct i2c_msg msgs[2] = {{.addr = adr,  .flags = 0,
488c2ecf20Sopenharmony_ci				   .buf  = wbuf, .len   = wlen },
498c2ecf20Sopenharmony_ci				  {.addr = adr,  .flags = I2C_M_RD,
508c2ecf20Sopenharmony_ci				   .buf  = rbuf,  .len   = rlen } };
518c2ecf20Sopenharmony_ci	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct i2c_msg msg = {.addr = adr, .flags = 0,
578c2ecf20Sopenharmony_ci			      .buf = data, .len = len};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return (i2c_transfer(adap, &msg, 1) == 1) ? 0 : -1;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int i2c_write_reg(struct i2c_adapter *adap, u8 adr,
638c2ecf20Sopenharmony_ci			 u8 reg, u8 val)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	u8 msg[2] = {reg, val};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return i2c_write(adap, adr, msg, 2);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct i2c_msg msgs[1] = {{.addr = adr,  .flags = I2C_M_RD,
738c2ecf20Sopenharmony_ci				   .buf  = val,  .len   = 1 } };
748c2ecf20Sopenharmony_ci	return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
788c2ecf20Sopenharmony_ci			  u16 reg, u8 *val)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	u8 msg[2] = {reg >> 8, reg & 0xff};
818c2ecf20Sopenharmony_ci	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
828c2ecf20Sopenharmony_ci				   .buf  = msg, .len   = 2},
838c2ecf20Sopenharmony_ci				  {.addr = adr, .flags = I2C_M_RD,
848c2ecf20Sopenharmony_ci				   .buf  = val, .len   = 1} };
858c2ecf20Sopenharmony_ci	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int i2c_read_regs(struct i2c_adapter *adapter,
898c2ecf20Sopenharmony_ci			 u8 adr, u8 reg, u8 *val, u8 len)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct i2c_msg msgs[2] = {{.addr = adr,  .flags = 0,
928c2ecf20Sopenharmony_ci				   .buf  = &reg, .len   = 1},
938c2ecf20Sopenharmony_ci				  {.addr = adr,  .flags = I2C_M_RD,
948c2ecf20Sopenharmony_ci				   .buf  = val,  .len   = len} };
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	return i2c_read_regs(adapter, adr, reg, val, 1);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/****************************************************************************/
1058c2ecf20Sopenharmony_ci/* Demod/tuner attachment ***************************************************/
1068c2ecf20Sopenharmony_ci/****************************************************************************/
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic struct i2c_adapter *i2c_adapter_from_chan(struct ngene_channel *chan)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	/* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
1118c2ecf20Sopenharmony_ci	if (chan->number < 2)
1128c2ecf20Sopenharmony_ci		return &chan->dev->channel[0].i2c_adapter;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return &chan->dev->channel[1].i2c_adapter;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int tuner_attach_stv6110(struct ngene_channel *chan)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
1208c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
1218c2ecf20Sopenharmony_ci	struct stv090x_config *feconf = (struct stv090x_config *)
1228c2ecf20Sopenharmony_ci		chan->dev->card_info->fe_config[chan->number];
1238c2ecf20Sopenharmony_ci	struct stv6110x_config *tunerconf = (struct stv6110x_config *)
1248c2ecf20Sopenharmony_ci		chan->dev->card_info->tuner_config[chan->number];
1258c2ecf20Sopenharmony_ci	const struct stv6110x_devctl *ctl;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c);
1288c2ecf20Sopenharmony_ci	if (ctl == NULL) {
1298c2ecf20Sopenharmony_ci		dev_err(pdev, "No STV6110X found!\n");
1308c2ecf20Sopenharmony_ci		return -ENODEV;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	feconf->tuner_init          = ctl->tuner_init;
1348c2ecf20Sopenharmony_ci	feconf->tuner_sleep         = ctl->tuner_sleep;
1358c2ecf20Sopenharmony_ci	feconf->tuner_set_mode      = ctl->tuner_set_mode;
1368c2ecf20Sopenharmony_ci	feconf->tuner_set_frequency = ctl->tuner_set_frequency;
1378c2ecf20Sopenharmony_ci	feconf->tuner_get_frequency = ctl->tuner_get_frequency;
1388c2ecf20Sopenharmony_ci	feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
1398c2ecf20Sopenharmony_ci	feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
1408c2ecf20Sopenharmony_ci	feconf->tuner_set_bbgain    = ctl->tuner_set_bbgain;
1418c2ecf20Sopenharmony_ci	feconf->tuner_get_bbgain    = ctl->tuner_get_bbgain;
1428c2ecf20Sopenharmony_ci	feconf->tuner_set_refclk    = ctl->tuner_set_refclk;
1438c2ecf20Sopenharmony_ci	feconf->tuner_get_status    = ctl->tuner_get_status;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int tuner_attach_stv6111(struct ngene_channel *chan)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
1518c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
1528c2ecf20Sopenharmony_ci	struct dvb_frontend *fe;
1538c2ecf20Sopenharmony_ci	u8 adr = 4 + ((chan->number & 1) ? 0x63 : 0x60);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	fe = dvb_attach(stv6111_attach, chan->fe, i2c, adr);
1568c2ecf20Sopenharmony_ci	if (!fe) {
1578c2ecf20Sopenharmony_ci		fe = dvb_attach(stv6111_attach, chan->fe, i2c, adr & ~4);
1588c2ecf20Sopenharmony_ci		if (!fe) {
1598c2ecf20Sopenharmony_ci			dev_err(pdev, "stv6111_attach() failed!\n");
1608c2ecf20Sopenharmony_ci			return -ENODEV;
1618c2ecf20Sopenharmony_ci		}
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct ngene_channel *chan = fe->sec_priv;
1698c2ecf20Sopenharmony_ci	int status;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (enable) {
1728c2ecf20Sopenharmony_ci		down(&chan->dev->pll_mutex);
1738c2ecf20Sopenharmony_ci		status = chan->gate_ctrl(fe, 1);
1748c2ecf20Sopenharmony_ci	} else {
1758c2ecf20Sopenharmony_ci		status = chan->gate_ctrl(fe, 0);
1768c2ecf20Sopenharmony_ci		up(&chan->dev->pll_mutex);
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	return status;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int tuner_attach_tda18271(struct ngene_channel *chan)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
1848c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
1858c2ecf20Sopenharmony_ci	struct dvb_frontend *fe;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (chan->fe->ops.i2c_gate_ctrl)
1888c2ecf20Sopenharmony_ci		chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
1898c2ecf20Sopenharmony_ci	fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60);
1908c2ecf20Sopenharmony_ci	if (chan->fe->ops.i2c_gate_ctrl)
1918c2ecf20Sopenharmony_ci		chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
1928c2ecf20Sopenharmony_ci	if (!fe) {
1938c2ecf20Sopenharmony_ci		dev_err(pdev, "No TDA18271 found!\n");
1948c2ecf20Sopenharmony_ci		return -ENODEV;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return 0;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int tuner_tda18212_ping(struct ngene_channel *chan,
2018c2ecf20Sopenharmony_ci			       struct i2c_adapter *i2c,
2028c2ecf20Sopenharmony_ci			       unsigned short adr)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
2058c2ecf20Sopenharmony_ci	u8 tda_id[2];
2068c2ecf20Sopenharmony_ci	u8 subaddr = 0x00;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	dev_dbg(pdev, "stv0367-tda18212 tuner ping\n");
2098c2ecf20Sopenharmony_ci	if (chan->fe->ops.i2c_gate_ctrl)
2108c2ecf20Sopenharmony_ci		chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (i2c_read_regs(i2c, adr, subaddr, tda_id, sizeof(tda_id)) < 0)
2138c2ecf20Sopenharmony_ci		dev_dbg(pdev, "tda18212 ping 1 fail\n");
2148c2ecf20Sopenharmony_ci	if (i2c_read_regs(i2c, adr, subaddr, tda_id, sizeof(tda_id)) < 0)
2158c2ecf20Sopenharmony_ci		dev_warn(pdev, "tda18212 ping failed, expect problems\n");
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (chan->fe->ops.i2c_gate_ctrl)
2188c2ecf20Sopenharmony_ci		chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic int tuner_attach_tda18212(struct ngene_channel *chan, u32 dmdtype)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
2268c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
2278c2ecf20Sopenharmony_ci	struct i2c_client *client;
2288c2ecf20Sopenharmony_ci	struct tda18212_config config = {
2298c2ecf20Sopenharmony_ci		.fe = chan->fe,
2308c2ecf20Sopenharmony_ci		.if_dvbt_6 = 3550,
2318c2ecf20Sopenharmony_ci		.if_dvbt_7 = 3700,
2328c2ecf20Sopenharmony_ci		.if_dvbt_8 = 4150,
2338c2ecf20Sopenharmony_ci		.if_dvbt2_6 = 3250,
2348c2ecf20Sopenharmony_ci		.if_dvbt2_7 = 4000,
2358c2ecf20Sopenharmony_ci		.if_dvbt2_8 = 4000,
2368c2ecf20Sopenharmony_ci		.if_dvbc = 5000,
2378c2ecf20Sopenharmony_ci	};
2388c2ecf20Sopenharmony_ci	u8 addr = (chan->number & 1) ? 0x63 : 0x60;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/*
2418c2ecf20Sopenharmony_ci	 * due to a hardware quirk with the I2C gate on the stv0367+tda18212
2428c2ecf20Sopenharmony_ci	 * combo, the tda18212 must be probed by reading it's id _twice_ when
2438c2ecf20Sopenharmony_ci	 * cold started, or it very likely will fail.
2448c2ecf20Sopenharmony_ci	 */
2458c2ecf20Sopenharmony_ci	if (dmdtype == DEMOD_TYPE_STV0367)
2468c2ecf20Sopenharmony_ci		tuner_tda18212_ping(chan, i2c, addr);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* perform tuner probe/init/attach */
2498c2ecf20Sopenharmony_ci	client = dvb_module_probe("tda18212", NULL, i2c, addr, &config);
2508c2ecf20Sopenharmony_ci	if (!client)
2518c2ecf20Sopenharmony_ci		goto err;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	chan->i2c_client[0] = client;
2548c2ecf20Sopenharmony_ci	chan->i2c_client_fe = 1;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return 0;
2578c2ecf20Sopenharmony_cierr:
2588c2ecf20Sopenharmony_ci	dev_err(pdev, "TDA18212 tuner not found. Device is not fully operational.\n");
2598c2ecf20Sopenharmony_ci	return -ENODEV;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic int tuner_attach_probe(struct ngene_channel *chan)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	switch (chan->demod_type) {
2658c2ecf20Sopenharmony_ci	case DEMOD_TYPE_STV090X:
2668c2ecf20Sopenharmony_ci		return tuner_attach_stv6110(chan);
2678c2ecf20Sopenharmony_ci	case DEMOD_TYPE_DRXK:
2688c2ecf20Sopenharmony_ci		return tuner_attach_tda18271(chan);
2698c2ecf20Sopenharmony_ci	case DEMOD_TYPE_STV0367:
2708c2ecf20Sopenharmony_ci	case DEMOD_TYPE_SONY_CT2:
2718c2ecf20Sopenharmony_ci	case DEMOD_TYPE_SONY_ISDBT:
2728c2ecf20Sopenharmony_ci	case DEMOD_TYPE_SONY_C2T2:
2738c2ecf20Sopenharmony_ci	case DEMOD_TYPE_SONY_C2T2I:
2748c2ecf20Sopenharmony_ci		return tuner_attach_tda18212(chan, chan->demod_type);
2758c2ecf20Sopenharmony_ci	case DEMOD_TYPE_STV0910:
2768c2ecf20Sopenharmony_ci		return tuner_attach_stv6111(chan);
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return -EINVAL;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic int demod_attach_stv0900(struct ngene_channel *chan)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
2858c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
2868c2ecf20Sopenharmony_ci	struct stv090x_config *feconf = (struct stv090x_config *)
2878c2ecf20Sopenharmony_ci		chan->dev->card_info->fe_config[chan->number];
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	chan->fe = dvb_attach(stv090x_attach, feconf, i2c,
2908c2ecf20Sopenharmony_ci			(chan->number & 1) == 0 ? STV090x_DEMODULATOR_0
2918c2ecf20Sopenharmony_ci						: STV090x_DEMODULATOR_1);
2928c2ecf20Sopenharmony_ci	if (chan->fe == NULL) {
2938c2ecf20Sopenharmony_ci		dev_err(pdev, "No STV0900 found!\n");
2948c2ecf20Sopenharmony_ci		return -ENODEV;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* store channel info */
2988c2ecf20Sopenharmony_ci	if (feconf->tuner_i2c_lock)
2998c2ecf20Sopenharmony_ci		chan->fe->analog_demod_priv = chan;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0,
3028c2ecf20Sopenharmony_ci			0, chan->dev->card_info->lnb[chan->number])) {
3038c2ecf20Sopenharmony_ci		dev_err(pdev, "No LNBH24 found!\n");
3048c2ecf20Sopenharmony_ci		dvb_frontend_detach(chan->fe);
3058c2ecf20Sopenharmony_ci		chan->fe = NULL;
3068c2ecf20Sopenharmony_ci		return -ENODEV;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return 0;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic struct stv0910_cfg stv0910_p = {
3138c2ecf20Sopenharmony_ci	.adr      = 0x68,
3148c2ecf20Sopenharmony_ci	.parallel = 1,
3158c2ecf20Sopenharmony_ci	.rptlvl   = 4,
3168c2ecf20Sopenharmony_ci	.clk      = 30000000,
3178c2ecf20Sopenharmony_ci	.tsspeed  = 0x28,
3188c2ecf20Sopenharmony_ci};
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic struct lnbh25_config lnbh25_cfg = {
3218c2ecf20Sopenharmony_ci	.i2c_address = 0x0c << 1,
3228c2ecf20Sopenharmony_ci	.data2_config = LNBH25_TEN
3238c2ecf20Sopenharmony_ci};
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic int demod_attach_stv0910(struct ngene_channel *chan,
3268c2ecf20Sopenharmony_ci				struct i2c_adapter *i2c)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
3298c2ecf20Sopenharmony_ci	struct stv0910_cfg cfg = stv0910_p;
3308c2ecf20Sopenharmony_ci	struct lnbh25_config lnbcfg = lnbh25_cfg;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	chan->fe = dvb_attach(stv0910_attach, i2c, &cfg, (chan->number & 1));
3338c2ecf20Sopenharmony_ci	if (!chan->fe) {
3348c2ecf20Sopenharmony_ci		cfg.adr = 0x6c;
3358c2ecf20Sopenharmony_ci		chan->fe = dvb_attach(stv0910_attach, i2c,
3368c2ecf20Sopenharmony_ci				      &cfg, (chan->number & 1));
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci	if (!chan->fe) {
3398c2ecf20Sopenharmony_ci		dev_err(pdev, "stv0910_attach() failed!\n");
3408c2ecf20Sopenharmony_ci		return -ENODEV;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/*
3448c2ecf20Sopenharmony_ci	 * attach lnbh25 - leftshift by one as the lnbh25 driver expects 8bit
3458c2ecf20Sopenharmony_ci	 * i2c addresses
3468c2ecf20Sopenharmony_ci	 */
3478c2ecf20Sopenharmony_ci	lnbcfg.i2c_address = (((chan->number & 1) ? 0x0d : 0x0c) << 1);
3488c2ecf20Sopenharmony_ci	if (!dvb_attach(lnbh25_attach, chan->fe, &lnbcfg, i2c)) {
3498c2ecf20Sopenharmony_ci		lnbcfg.i2c_address = (((chan->number & 1) ? 0x09 : 0x08) << 1);
3508c2ecf20Sopenharmony_ci		if (!dvb_attach(lnbh25_attach, chan->fe, &lnbcfg, i2c)) {
3518c2ecf20Sopenharmony_ci			dev_err(pdev, "lnbh25_attach() failed!\n");
3528c2ecf20Sopenharmony_ci			dvb_frontend_detach(chan->fe);
3538c2ecf20Sopenharmony_ci			chan->fe = NULL;
3548c2ecf20Sopenharmony_ci			return -ENODEV;
3558c2ecf20Sopenharmony_ci		}
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic struct stv0367_config ddb_stv0367_config[] = {
3628c2ecf20Sopenharmony_ci	{
3638c2ecf20Sopenharmony_ci		.demod_address = 0x1f,
3648c2ecf20Sopenharmony_ci		.xtal = 27000000,
3658c2ecf20Sopenharmony_ci		.if_khz = 0,
3668c2ecf20Sopenharmony_ci		.if_iq_mode = FE_TER_NORMAL_IF_TUNER,
3678c2ecf20Sopenharmony_ci		.ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
3688c2ecf20Sopenharmony_ci		.clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
3698c2ecf20Sopenharmony_ci	}, {
3708c2ecf20Sopenharmony_ci		.demod_address = 0x1e,
3718c2ecf20Sopenharmony_ci		.xtal = 27000000,
3728c2ecf20Sopenharmony_ci		.if_khz = 0,
3738c2ecf20Sopenharmony_ci		.if_iq_mode = FE_TER_NORMAL_IF_TUNER,
3748c2ecf20Sopenharmony_ci		.ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
3758c2ecf20Sopenharmony_ci		.clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
3768c2ecf20Sopenharmony_ci	},
3778c2ecf20Sopenharmony_ci};
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic int demod_attach_stv0367(struct ngene_channel *chan,
3808c2ecf20Sopenharmony_ci				struct i2c_adapter *i2c)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	chan->fe = dvb_attach(stv0367ddb_attach,
3858c2ecf20Sopenharmony_ci			      &ddb_stv0367_config[(chan->number & 1)], i2c);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	if (!chan->fe) {
3888c2ecf20Sopenharmony_ci		dev_err(pdev, "stv0367ddb_attach() failed!\n");
3898c2ecf20Sopenharmony_ci		return -ENODEV;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	chan->fe->sec_priv = chan;
3938c2ecf20Sopenharmony_ci	chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
3948c2ecf20Sopenharmony_ci	chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
3958c2ecf20Sopenharmony_ci	return 0;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int demod_attach_cxd28xx(struct ngene_channel *chan,
3998c2ecf20Sopenharmony_ci				struct i2c_adapter *i2c, int osc24)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
4028c2ecf20Sopenharmony_ci	struct cxd2841er_config cfg;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* the cxd2841er driver expects 8bit/shifted I2C addresses */
4058c2ecf20Sopenharmony_ci	cfg.i2c_addr = ((chan->number & 1) ? 0x6d : 0x6c) << 1;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	cfg.xtal = osc24 ? SONY_XTAL_24000 : SONY_XTAL_20500;
4088c2ecf20Sopenharmony_ci	cfg.flags = CXD2841ER_AUTO_IFHZ | CXD2841ER_EARLY_TUNE |
4098c2ecf20Sopenharmony_ci		CXD2841ER_NO_WAIT_LOCK | CXD2841ER_NO_AGCNEG |
4108c2ecf20Sopenharmony_ci		CXD2841ER_TSBITS | CXD2841ER_TS_SERIAL;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* attach frontend */
4138c2ecf20Sopenharmony_ci	chan->fe = dvb_attach(cxd2841er_attach_t_c, &cfg, i2c);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (!chan->fe) {
4168c2ecf20Sopenharmony_ci		dev_err(pdev, "CXD28XX attach failed!\n");
4178c2ecf20Sopenharmony_ci		return -ENODEV;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	chan->fe->sec_priv = chan;
4218c2ecf20Sopenharmony_ci	chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
4228c2ecf20Sopenharmony_ci	chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct ngene_channel *chan = fe->analog_demod_priv;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	if (lock)
4318c2ecf20Sopenharmony_ci		down(&chan->dev->pll_mutex);
4328c2ecf20Sopenharmony_ci	else
4338c2ecf20Sopenharmony_ci		up(&chan->dev->pll_mutex);
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic int port_has_stv0900(struct i2c_adapter *i2c, int port)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	u8 val;
4398c2ecf20Sopenharmony_ci	if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0)
4408c2ecf20Sopenharmony_ci		return 0;
4418c2ecf20Sopenharmony_ci	return 1;
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_cistatic int port_has_drxk(struct i2c_adapter *i2c, int port)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	u8 val;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (i2c_read(i2c, 0x29+port, &val) < 0)
4498c2ecf20Sopenharmony_ci		return 0;
4508c2ecf20Sopenharmony_ci	return 1;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic int port_has_stv0367(struct i2c_adapter *i2c)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	u8 val;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (i2c_read_reg16(i2c, 0x1e, 0xf000, &val) < 0)
4588c2ecf20Sopenharmony_ci		return 0;
4598c2ecf20Sopenharmony_ci	if (val != 0x60)
4608c2ecf20Sopenharmony_ci		return 0;
4618c2ecf20Sopenharmony_ci	if (i2c_read_reg16(i2c, 0x1f, 0xf000, &val) < 0)
4628c2ecf20Sopenharmony_ci		return 0;
4638c2ecf20Sopenharmony_ci	if (val != 0x60)
4648c2ecf20Sopenharmony_ci		return 0;
4658c2ecf20Sopenharmony_ci	return 1;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ciint ngene_port_has_cxd2099(struct i2c_adapter *i2c, u8 *type)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	u8 val;
4718c2ecf20Sopenharmony_ci	u8 probe[4] = { 0xe0, 0x00, 0x00, 0x00 }, data[4];
4728c2ecf20Sopenharmony_ci	struct i2c_msg msgs[2] = {{ .addr = 0x40,  .flags = 0,
4738c2ecf20Sopenharmony_ci				    .buf  = probe, .len   = 4 },
4748c2ecf20Sopenharmony_ci				  { .addr = 0x40,  .flags = I2C_M_RD,
4758c2ecf20Sopenharmony_ci				    .buf  = data,  .len   = 4 } };
4768c2ecf20Sopenharmony_ci	val = i2c_transfer(i2c, msgs, 2);
4778c2ecf20Sopenharmony_ci	if (val != 2)
4788c2ecf20Sopenharmony_ci		return 0;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (data[0] == 0x02 && data[1] == 0x2b && data[3] == 0x43)
4818c2ecf20Sopenharmony_ci		*type = 2;
4828c2ecf20Sopenharmony_ci	else
4838c2ecf20Sopenharmony_ci		*type = 1;
4848c2ecf20Sopenharmony_ci	return 1;
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic int demod_attach_drxk(struct ngene_channel *chan,
4888c2ecf20Sopenharmony_ci			     struct i2c_adapter *i2c)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
4918c2ecf20Sopenharmony_ci	struct drxk_config config;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	memset(&config, 0, sizeof(config));
4948c2ecf20Sopenharmony_ci	config.microcode_name = "drxk_a3.mc";
4958c2ecf20Sopenharmony_ci	config.qam_demod_parameter_count = 4;
4968c2ecf20Sopenharmony_ci	config.adr = 0x29 + (chan->number ^ 2);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	chan->fe = dvb_attach(drxk_attach, &config, i2c);
4998c2ecf20Sopenharmony_ci	if (!chan->fe) {
5008c2ecf20Sopenharmony_ci		dev_err(pdev, "No DRXK found!\n");
5018c2ecf20Sopenharmony_ci		return -ENODEV;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci	chan->fe->sec_priv = chan;
5048c2ecf20Sopenharmony_ci	chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
5058c2ecf20Sopenharmony_ci	chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
5068c2ecf20Sopenharmony_ci	return 0;
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci/****************************************************************************/
5108c2ecf20Sopenharmony_ci/* XO2 related lists and functions ******************************************/
5118c2ecf20Sopenharmony_ci/****************************************************************************/
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic char *xo2names[] = {
5148c2ecf20Sopenharmony_ci	"DUAL DVB-S2",
5158c2ecf20Sopenharmony_ci	"DUAL DVB-C/T/T2",
5168c2ecf20Sopenharmony_ci	"DUAL DVB-ISDBT",
5178c2ecf20Sopenharmony_ci	"DUAL DVB-C/C2/T/T2",
5188c2ecf20Sopenharmony_ci	"DUAL ATSC",
5198c2ecf20Sopenharmony_ci	"DUAL DVB-C/C2/T/T2/I",
5208c2ecf20Sopenharmony_ci};
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic int init_xo2(struct ngene_channel *chan, struct i2c_adapter *i2c)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
5258c2ecf20Sopenharmony_ci	u8 addr = 0x10;
5268c2ecf20Sopenharmony_ci	u8 val, data[2];
5278c2ecf20Sopenharmony_ci	int res;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	res = i2c_read_regs(i2c, addr, 0x04, data, 2);
5308c2ecf20Sopenharmony_ci	if (res < 0)
5318c2ecf20Sopenharmony_ci		return res;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (data[0] != 0x01)  {
5348c2ecf20Sopenharmony_ci		dev_info(pdev, "Invalid XO2 on channel %d\n", chan->number);
5358c2ecf20Sopenharmony_ci		return -1;
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	i2c_read_reg(i2c, addr, 0x08, &val);
5398c2ecf20Sopenharmony_ci	if (val != 0) {
5408c2ecf20Sopenharmony_ci		i2c_write_reg(i2c, addr, 0x08, 0x00);
5418c2ecf20Sopenharmony_ci		msleep(100);
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci	/* Enable tuner power, disable pll, reset demods */
5448c2ecf20Sopenharmony_ci	i2c_write_reg(i2c, addr, 0x08, 0x04);
5458c2ecf20Sopenharmony_ci	usleep_range(2000, 3000);
5468c2ecf20Sopenharmony_ci	/* Release demod resets */
5478c2ecf20Sopenharmony_ci	i2c_write_reg(i2c, addr, 0x08, 0x07);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	/*
5508c2ecf20Sopenharmony_ci	 * speed: 0=55,1=75,2=90,3=104 MBit/s
5518c2ecf20Sopenharmony_ci	 * Note: The ngene hardware must be run at 75 MBit/s compared
5528c2ecf20Sopenharmony_ci	 * to more modern ddbridge hardware which runs at 90 MBit/s,
5538c2ecf20Sopenharmony_ci	 * else there will be issues with the data transport and non-
5548c2ecf20Sopenharmony_ci	 * working secondary/slave demods/tuners.
5558c2ecf20Sopenharmony_ci	 */
5568c2ecf20Sopenharmony_ci	i2c_write_reg(i2c, addr, 0x09, 1);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	i2c_write_reg(i2c, addr, 0x0a, 0x01);
5598c2ecf20Sopenharmony_ci	i2c_write_reg(i2c, addr, 0x0b, 0x01);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	usleep_range(2000, 3000);
5628c2ecf20Sopenharmony_ci	/* Start XO2 PLL */
5638c2ecf20Sopenharmony_ci	i2c_write_reg(i2c, addr, 0x08, 0x87);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	return 0;
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic int port_has_xo2(struct i2c_adapter *i2c, u8 *type, u8 *id)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	u8 probe[1] = { 0x00 }, data[4];
5718c2ecf20Sopenharmony_ci	u8 addr = 0x10;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	*type = NGENE_XO2_TYPE_NONE;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (i2c_io(i2c, addr, probe, 1, data, 4))
5768c2ecf20Sopenharmony_ci		return 0;
5778c2ecf20Sopenharmony_ci	if (data[0] == 'D' && data[1] == 'F') {
5788c2ecf20Sopenharmony_ci		*id = data[2];
5798c2ecf20Sopenharmony_ci		*type = NGENE_XO2_TYPE_DUOFLEX;
5808c2ecf20Sopenharmony_ci		return 1;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci	if (data[0] == 'C' && data[1] == 'I') {
5838c2ecf20Sopenharmony_ci		*id = data[2];
5848c2ecf20Sopenharmony_ci		*type = NGENE_XO2_TYPE_CI;
5858c2ecf20Sopenharmony_ci		return 1;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci	return 0;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci/****************************************************************************/
5918c2ecf20Sopenharmony_ci/* Probing and port/channel handling ****************************************/
5928c2ecf20Sopenharmony_ci/****************************************************************************/
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_cistatic int cineS2_probe(struct ngene_channel *chan)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
5978c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c = i2c_adapter_from_chan(chan);
5988c2ecf20Sopenharmony_ci	struct stv090x_config *fe_conf;
5998c2ecf20Sopenharmony_ci	u8 buf[3];
6008c2ecf20Sopenharmony_ci	u8 xo2_type, xo2_id, xo2_demodtype;
6018c2ecf20Sopenharmony_ci	u8 sony_osc24 = 0;
6028c2ecf20Sopenharmony_ci	struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
6038c2ecf20Sopenharmony_ci	int rc;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (port_has_xo2(i2c, &xo2_type, &xo2_id)) {
6068c2ecf20Sopenharmony_ci		xo2_id >>= 2;
6078c2ecf20Sopenharmony_ci		dev_dbg(pdev, "XO2 on channel %d (type %d, id %d)\n",
6088c2ecf20Sopenharmony_ci			chan->number, xo2_type, xo2_id);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci		switch (xo2_type) {
6118c2ecf20Sopenharmony_ci		case NGENE_XO2_TYPE_DUOFLEX:
6128c2ecf20Sopenharmony_ci			if (chan->number & 1)
6138c2ecf20Sopenharmony_ci				dev_dbg(pdev,
6148c2ecf20Sopenharmony_ci					"skipping XO2 init on odd channel %d",
6158c2ecf20Sopenharmony_ci					chan->number);
6168c2ecf20Sopenharmony_ci			else
6178c2ecf20Sopenharmony_ci				init_xo2(chan, i2c);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci			xo2_demodtype = DEMOD_TYPE_XO2 + xo2_id;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci			switch (xo2_demodtype) {
6228c2ecf20Sopenharmony_ci			case DEMOD_TYPE_SONY_CT2:
6238c2ecf20Sopenharmony_ci			case DEMOD_TYPE_SONY_ISDBT:
6248c2ecf20Sopenharmony_ci			case DEMOD_TYPE_SONY_C2T2:
6258c2ecf20Sopenharmony_ci			case DEMOD_TYPE_SONY_C2T2I:
6268c2ecf20Sopenharmony_ci				dev_info(pdev, "%s (XO2) on channel %d\n",
6278c2ecf20Sopenharmony_ci					 xo2names[xo2_id], chan->number);
6288c2ecf20Sopenharmony_ci				chan->demod_type = xo2_demodtype;
6298c2ecf20Sopenharmony_ci				if (xo2_demodtype == DEMOD_TYPE_SONY_C2T2I)
6308c2ecf20Sopenharmony_ci					sony_osc24 = 1;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci				demod_attach_cxd28xx(chan, i2c, sony_osc24);
6338c2ecf20Sopenharmony_ci				break;
6348c2ecf20Sopenharmony_ci			case DEMOD_TYPE_STV0910:
6358c2ecf20Sopenharmony_ci				dev_info(pdev, "%s (XO2) on channel %d\n",
6368c2ecf20Sopenharmony_ci					 xo2names[xo2_id], chan->number);
6378c2ecf20Sopenharmony_ci				chan->demod_type = xo2_demodtype;
6388c2ecf20Sopenharmony_ci				demod_attach_stv0910(chan, i2c);
6398c2ecf20Sopenharmony_ci				break;
6408c2ecf20Sopenharmony_ci			default:
6418c2ecf20Sopenharmony_ci				dev_warn(pdev,
6428c2ecf20Sopenharmony_ci					 "Unsupported XO2 module on channel %d\n",
6438c2ecf20Sopenharmony_ci					 chan->number);
6448c2ecf20Sopenharmony_ci				return -ENODEV;
6458c2ecf20Sopenharmony_ci			}
6468c2ecf20Sopenharmony_ci			break;
6478c2ecf20Sopenharmony_ci		case NGENE_XO2_TYPE_CI:
6488c2ecf20Sopenharmony_ci			dev_info(pdev, "DuoFlex CI modules not supported\n");
6498c2ecf20Sopenharmony_ci			return -ENODEV;
6508c2ecf20Sopenharmony_ci		default:
6518c2ecf20Sopenharmony_ci			dev_info(pdev, "Unsupported XO2 module type\n");
6528c2ecf20Sopenharmony_ci			return -ENODEV;
6538c2ecf20Sopenharmony_ci		}
6548c2ecf20Sopenharmony_ci	} else if (port_has_stv0900(i2c, chan->number)) {
6558c2ecf20Sopenharmony_ci		chan->demod_type = DEMOD_TYPE_STV090X;
6568c2ecf20Sopenharmony_ci		fe_conf = chan->dev->card_info->fe_config[chan->number];
6578c2ecf20Sopenharmony_ci		/* demod found, attach it */
6588c2ecf20Sopenharmony_ci		rc = demod_attach_stv0900(chan);
6598c2ecf20Sopenharmony_ci		if (rc < 0 || chan->number < 2)
6608c2ecf20Sopenharmony_ci			return rc;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci		/* demod #2: reprogram outputs DPN1 & DPN2 */
6638c2ecf20Sopenharmony_ci		i2c_msg.addr = fe_conf->address;
6648c2ecf20Sopenharmony_ci		i2c_msg.len = 3;
6658c2ecf20Sopenharmony_ci		buf[0] = 0xf1;
6668c2ecf20Sopenharmony_ci		switch (chan->number) {
6678c2ecf20Sopenharmony_ci		case 2:
6688c2ecf20Sopenharmony_ci			buf[1] = 0x5c;
6698c2ecf20Sopenharmony_ci			buf[2] = 0xc2;
6708c2ecf20Sopenharmony_ci			break;
6718c2ecf20Sopenharmony_ci		case 3:
6728c2ecf20Sopenharmony_ci			buf[1] = 0x61;
6738c2ecf20Sopenharmony_ci			buf[2] = 0xcc;
6748c2ecf20Sopenharmony_ci			break;
6758c2ecf20Sopenharmony_ci		default:
6768c2ecf20Sopenharmony_ci			return -ENODEV;
6778c2ecf20Sopenharmony_ci		}
6788c2ecf20Sopenharmony_ci		rc = i2c_transfer(i2c, &i2c_msg, 1);
6798c2ecf20Sopenharmony_ci		if (rc != 1) {
6808c2ecf20Sopenharmony_ci			dev_err(pdev, "Could not setup DPNx\n");
6818c2ecf20Sopenharmony_ci			return -EIO;
6828c2ecf20Sopenharmony_ci		}
6838c2ecf20Sopenharmony_ci	} else if (port_has_drxk(i2c, chan->number^2)) {
6848c2ecf20Sopenharmony_ci		chan->demod_type = DEMOD_TYPE_DRXK;
6858c2ecf20Sopenharmony_ci		demod_attach_drxk(chan, i2c);
6868c2ecf20Sopenharmony_ci	} else if (port_has_stv0367(i2c)) {
6878c2ecf20Sopenharmony_ci		chan->demod_type = DEMOD_TYPE_STV0367;
6888c2ecf20Sopenharmony_ci		dev_info(pdev, "STV0367 on channel %d\n", chan->number);
6898c2ecf20Sopenharmony_ci		demod_attach_stv0367(chan, i2c);
6908c2ecf20Sopenharmony_ci	} else {
6918c2ecf20Sopenharmony_ci		dev_info(pdev, "No demod found on chan %d\n", chan->number);
6928c2ecf20Sopenharmony_ci		return -ENODEV;
6938c2ecf20Sopenharmony_ci	}
6948c2ecf20Sopenharmony_ci	return 0;
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic struct lgdt330x_config aver_m780 = {
6998c2ecf20Sopenharmony_ci	.demod_chip    = LGDT3303,
7008c2ecf20Sopenharmony_ci	.serial_mpeg   = 0x00, /* PARALLEL */
7018c2ecf20Sopenharmony_ci	.clock_polarity_flip = 1,
7028c2ecf20Sopenharmony_ci};
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic struct mt2131_config m780_tunerconfig = {
7058c2ecf20Sopenharmony_ci	0xc0 >> 1
7068c2ecf20Sopenharmony_ci};
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci/* A single func to attach the demo and tuner, rather than
7098c2ecf20Sopenharmony_ci * use two sep funcs like the current design mandates.
7108c2ecf20Sopenharmony_ci */
7118c2ecf20Sopenharmony_cistatic int demod_attach_lg330x(struct ngene_channel *chan)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	chan->fe = dvb_attach(lgdt330x_attach, &aver_m780,
7168c2ecf20Sopenharmony_ci			      0xb2 >> 1, &chan->i2c_adapter);
7178c2ecf20Sopenharmony_ci	if (chan->fe == NULL) {
7188c2ecf20Sopenharmony_ci		dev_err(pdev, "No LGDT330x found!\n");
7198c2ecf20Sopenharmony_ci		return -ENODEV;
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter,
7238c2ecf20Sopenharmony_ci		   &m780_tunerconfig, 0);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	return (chan->fe) ? 0 : -ENODEV;
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cistatic int demod_attach_drxd(struct ngene_channel *chan)
7298c2ecf20Sopenharmony_ci{
7308c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
7318c2ecf20Sopenharmony_ci	struct drxd_config *feconf;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	feconf = chan->dev->card_info->fe_config[chan->number];
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	chan->fe = dvb_attach(drxd_attach, feconf, chan,
7368c2ecf20Sopenharmony_ci			&chan->i2c_adapter, &chan->dev->pci_dev->dev);
7378c2ecf20Sopenharmony_ci	if (!chan->fe) {
7388c2ecf20Sopenharmony_ci		dev_err(pdev, "No DRXD found!\n");
7398c2ecf20Sopenharmony_ci		return -ENODEV;
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci	return 0;
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_cistatic int tuner_attach_dtt7520x(struct ngene_channel *chan)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
7478c2ecf20Sopenharmony_ci	struct drxd_config *feconf;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	feconf = chan->dev->card_info->fe_config[chan->number];
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address,
7528c2ecf20Sopenharmony_ci			&chan->i2c_adapter,
7538c2ecf20Sopenharmony_ci			feconf->pll_type)) {
7548c2ecf20Sopenharmony_ci		dev_err(pdev, "No pll(%d) found!\n", feconf->pll_type);
7558c2ecf20Sopenharmony_ci		return -ENODEV;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci	return 0;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci/****************************************************************************/
7618c2ecf20Sopenharmony_ci/* EEPROM TAGS **************************************************************/
7628c2ecf20Sopenharmony_ci/****************************************************************************/
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci#define MICNG_EE_START      0x0100
7658c2ecf20Sopenharmony_ci#define MICNG_EE_END        0x0FF0
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci#define MICNG_EETAG_END0    0x0000
7688c2ecf20Sopenharmony_ci#define MICNG_EETAG_END1    0xFFFF
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci/* 0x0001 - 0x000F reserved for housekeeping */
7718c2ecf20Sopenharmony_ci/* 0xFFFF - 0xFFFE reserved for housekeeping */
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci/* Micronas assigned tags
7748c2ecf20Sopenharmony_ci   EEProm tags for hardware support */
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci#define MICNG_EETAG_DRXD1_OSCDEVIATION  0x1000  /* 2 Bytes data */
7778c2ecf20Sopenharmony_ci#define MICNG_EETAG_DRXD2_OSCDEVIATION  0x1001  /* 2 Bytes data */
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci#define MICNG_EETAG_MT2060_1_1STIF      0x1100  /* 2 Bytes data */
7808c2ecf20Sopenharmony_ci#define MICNG_EETAG_MT2060_2_1STIF      0x1101  /* 2 Bytes data */
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci/* Tag range for OEMs */
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci#define MICNG_EETAG_OEM_FIRST  0xC000
7858c2ecf20Sopenharmony_ci#define MICNG_EETAG_OEM_LAST   0xFFEF
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_cistatic int i2c_write_eeprom(struct i2c_adapter *adapter,
7888c2ecf20Sopenharmony_ci			    u8 adr, u16 reg, u8 data)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	struct device *pdev = adapter->dev.parent;
7918c2ecf20Sopenharmony_ci	u8 m[3] = {(reg >> 8), (reg & 0xff), data};
7928c2ecf20Sopenharmony_ci	struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m,
7938c2ecf20Sopenharmony_ci			      .len = sizeof(m)};
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if (i2c_transfer(adapter, &msg, 1) != 1) {
7968c2ecf20Sopenharmony_ci		dev_err(pdev, "Error writing EEPROM!\n");
7978c2ecf20Sopenharmony_ci		return -EIO;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci	return 0;
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic int i2c_read_eeprom(struct i2c_adapter *adapter,
8038c2ecf20Sopenharmony_ci			   u8 adr, u16 reg, u8 *data, int len)
8048c2ecf20Sopenharmony_ci{
8058c2ecf20Sopenharmony_ci	struct device *pdev = adapter->dev.parent;
8068c2ecf20Sopenharmony_ci	u8 msg[2] = {(reg >> 8), (reg & 0xff)};
8078c2ecf20Sopenharmony_ci	struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
8088c2ecf20Sopenharmony_ci				   .buf = msg, .len = 2 },
8098c2ecf20Sopenharmony_ci				  {.addr = adr, .flags = I2C_M_RD,
8108c2ecf20Sopenharmony_ci				   .buf = data, .len = len} };
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (i2c_transfer(adapter, msgs, 2) != 2) {
8138c2ecf20Sopenharmony_ci		dev_err(pdev, "Error reading EEPROM\n");
8148c2ecf20Sopenharmony_ci		return -EIO;
8158c2ecf20Sopenharmony_ci	}
8168c2ecf20Sopenharmony_ci	return 0;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic int ReadEEProm(struct i2c_adapter *adapter,
8208c2ecf20Sopenharmony_ci		      u16 Tag, u32 MaxLen, u8 *data, u32 *pLength)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	struct device *pdev = adapter->dev.parent;
8238c2ecf20Sopenharmony_ci	int status = 0;
8248c2ecf20Sopenharmony_ci	u16 Addr = MICNG_EE_START, Length, tag = 0;
8258c2ecf20Sopenharmony_ci	u8  EETag[3];
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
8288c2ecf20Sopenharmony_ci		if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
8298c2ecf20Sopenharmony_ci			return -1;
8308c2ecf20Sopenharmony_ci		tag = (EETag[0] << 8) | EETag[1];
8318c2ecf20Sopenharmony_ci		if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
8328c2ecf20Sopenharmony_ci			return -1;
8338c2ecf20Sopenharmony_ci		if (tag == Tag)
8348c2ecf20Sopenharmony_ci			break;
8358c2ecf20Sopenharmony_ci		Addr += sizeof(u16) + 1 + EETag[2];
8368c2ecf20Sopenharmony_ci	}
8378c2ecf20Sopenharmony_ci	if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
8388c2ecf20Sopenharmony_ci		dev_err(pdev, "Reached EOEE @ Tag = %04x Length = %3d\n",
8398c2ecf20Sopenharmony_ci			tag, EETag[2]);
8408c2ecf20Sopenharmony_ci		return -1;
8418c2ecf20Sopenharmony_ci	}
8428c2ecf20Sopenharmony_ci	Length = EETag[2];
8438c2ecf20Sopenharmony_ci	if (Length > MaxLen)
8448c2ecf20Sopenharmony_ci		Length = (u16) MaxLen;
8458c2ecf20Sopenharmony_ci	if (Length > 0) {
8468c2ecf20Sopenharmony_ci		Addr += sizeof(u16) + 1;
8478c2ecf20Sopenharmony_ci		status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length);
8488c2ecf20Sopenharmony_ci		if (!status) {
8498c2ecf20Sopenharmony_ci			*pLength = EETag[2];
8508c2ecf20Sopenharmony_ci#if 0
8518c2ecf20Sopenharmony_ci			if (Length < EETag[2])
8528c2ecf20Sopenharmony_ci				status = STATUS_BUFFER_OVERFLOW;
8538c2ecf20Sopenharmony_ci#endif
8548c2ecf20Sopenharmony_ci		}
8558c2ecf20Sopenharmony_ci	}
8568c2ecf20Sopenharmony_ci	return status;
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic int WriteEEProm(struct i2c_adapter *adapter,
8608c2ecf20Sopenharmony_ci		       u16 Tag, u32 Length, u8 *data)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	struct device *pdev = adapter->dev.parent;
8638c2ecf20Sopenharmony_ci	int status = 0;
8648c2ecf20Sopenharmony_ci	u16 Addr = MICNG_EE_START;
8658c2ecf20Sopenharmony_ci	u8 EETag[3];
8668c2ecf20Sopenharmony_ci	u16 tag = 0;
8678c2ecf20Sopenharmony_ci	int retry, i;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
8708c2ecf20Sopenharmony_ci		if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
8718c2ecf20Sopenharmony_ci			return -1;
8728c2ecf20Sopenharmony_ci		tag = (EETag[0] << 8) | EETag[1];
8738c2ecf20Sopenharmony_ci		if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
8748c2ecf20Sopenharmony_ci			return -1;
8758c2ecf20Sopenharmony_ci		if (tag == Tag)
8768c2ecf20Sopenharmony_ci			break;
8778c2ecf20Sopenharmony_ci		Addr += sizeof(u16) + 1 + EETag[2];
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci	if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
8808c2ecf20Sopenharmony_ci		dev_err(pdev, "Reached EOEE @ Tag = %04x Length = %3d\n",
8818c2ecf20Sopenharmony_ci			tag, EETag[2]);
8828c2ecf20Sopenharmony_ci		return -1;
8838c2ecf20Sopenharmony_ci	}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (Length > EETag[2])
8868c2ecf20Sopenharmony_ci		return -EINVAL;
8878c2ecf20Sopenharmony_ci	/* Note: We write the data one byte at a time to avoid
8888c2ecf20Sopenharmony_ci	   issues with page sizes. (which are different for
8898c2ecf20Sopenharmony_ci	   each manufacture and eeprom size)
8908c2ecf20Sopenharmony_ci	 */
8918c2ecf20Sopenharmony_ci	Addr += sizeof(u16) + 1;
8928c2ecf20Sopenharmony_ci	for (i = 0; i < Length; i++, Addr++) {
8938c2ecf20Sopenharmony_ci		status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		if (status)
8968c2ecf20Sopenharmony_ci			break;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci		/* Poll for finishing write cycle */
8998c2ecf20Sopenharmony_ci		retry = 10;
9008c2ecf20Sopenharmony_ci		while (retry) {
9018c2ecf20Sopenharmony_ci			u8 Tmp;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci			msleep(50);
9048c2ecf20Sopenharmony_ci			status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1);
9058c2ecf20Sopenharmony_ci			if (status)
9068c2ecf20Sopenharmony_ci				break;
9078c2ecf20Sopenharmony_ci			if (Tmp != data[i])
9088c2ecf20Sopenharmony_ci				dev_err(pdev, "eeprom write error\n");
9098c2ecf20Sopenharmony_ci			retry -= 1;
9108c2ecf20Sopenharmony_ci		}
9118c2ecf20Sopenharmony_ci		if (status) {
9128c2ecf20Sopenharmony_ci			dev_err(pdev, "Timeout polling eeprom\n");
9138c2ecf20Sopenharmony_ci			break;
9148c2ecf20Sopenharmony_ci		}
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci	return status;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_cistatic int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	int stat;
9228c2ecf20Sopenharmony_ci	u8 buf[2];
9238c2ecf20Sopenharmony_ci	u32 len = 0;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	stat = ReadEEProm(adapter, tag, 2, buf, &len);
9268c2ecf20Sopenharmony_ci	if (stat)
9278c2ecf20Sopenharmony_ci		return stat;
9288c2ecf20Sopenharmony_ci	if (len != 2)
9298c2ecf20Sopenharmony_ci		return -EINVAL;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	*data = (buf[0] << 8) | buf[1];
9328c2ecf20Sopenharmony_ci	return 0;
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data)
9368c2ecf20Sopenharmony_ci{
9378c2ecf20Sopenharmony_ci	int stat;
9388c2ecf20Sopenharmony_ci	u8 buf[2];
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	buf[0] = data >> 8;
9418c2ecf20Sopenharmony_ci	buf[1] = data & 0xff;
9428c2ecf20Sopenharmony_ci	stat = WriteEEProm(adapter, tag, 2, buf);
9438c2ecf20Sopenharmony_ci	if (stat)
9448c2ecf20Sopenharmony_ci		return stat;
9458c2ecf20Sopenharmony_ci	return 0;
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic s16 osc_deviation(void *priv, s16 deviation, int flag)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	struct ngene_channel *chan = priv;
9518c2ecf20Sopenharmony_ci	struct device *pdev = &chan->dev->pci_dev->dev;
9528c2ecf20Sopenharmony_ci	struct i2c_adapter *adap = &chan->i2c_adapter;
9538c2ecf20Sopenharmony_ci	u16 data = 0;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	if (flag) {
9568c2ecf20Sopenharmony_ci		data = (u16) deviation;
9578c2ecf20Sopenharmony_ci		dev_info(pdev, "write deviation %d\n",
9588c2ecf20Sopenharmony_ci			 deviation);
9598c2ecf20Sopenharmony_ci		eeprom_write_ushort(adap, 0x1000 + chan->number, data);
9608c2ecf20Sopenharmony_ci	} else {
9618c2ecf20Sopenharmony_ci		if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data))
9628c2ecf20Sopenharmony_ci			data = 0;
9638c2ecf20Sopenharmony_ci		dev_info(pdev, "read deviation %d\n",
9648c2ecf20Sopenharmony_ci			 (s16)data);
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	return (s16) data;
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci/****************************************************************************/
9718c2ecf20Sopenharmony_ci/* Switch control (I2C gates, etc.) *****************************************/
9728c2ecf20Sopenharmony_ci/****************************************************************************/
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cistatic struct stv090x_config fe_cineS2 = {
9768c2ecf20Sopenharmony_ci	.device         = STV0900,
9778c2ecf20Sopenharmony_ci	.demod_mode     = STV090x_DUAL,
9788c2ecf20Sopenharmony_ci	.clk_mode       = STV090x_CLK_EXT,
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	.xtal           = 27000000,
9818c2ecf20Sopenharmony_ci	.address        = 0x68,
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	.ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
9848c2ecf20Sopenharmony_ci	.ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	.repeater_level = STV090x_RPTLEVEL_16,
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	.adc1_range	= STV090x_ADC_1Vpp,
9898c2ecf20Sopenharmony_ci	.adc2_range	= STV090x_ADC_1Vpp,
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	.diseqc_envelope_mode = true,
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	.tuner_i2c_lock = cineS2_tuner_i2c_lock,
9948c2ecf20Sopenharmony_ci};
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_cistatic struct stv090x_config fe_cineS2_2 = {
9978c2ecf20Sopenharmony_ci	.device         = STV0900,
9988c2ecf20Sopenharmony_ci	.demod_mode     = STV090x_DUAL,
9998c2ecf20Sopenharmony_ci	.clk_mode       = STV090x_CLK_EXT,
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	.xtal           = 27000000,
10028c2ecf20Sopenharmony_ci	.address        = 0x69,
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	.ts1_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
10058c2ecf20Sopenharmony_ci	.ts2_mode       = STV090x_TSMODE_SERIAL_PUNCTURED,
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	.repeater_level = STV090x_RPTLEVEL_16,
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	.adc1_range	= STV090x_ADC_1Vpp,
10108c2ecf20Sopenharmony_ci	.adc2_range	= STV090x_ADC_1Vpp,
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	.diseqc_envelope_mode = true,
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	.tuner_i2c_lock = cineS2_tuner_i2c_lock,
10158c2ecf20Sopenharmony_ci};
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_cistatic struct stv6110x_config tuner_cineS2_0 = {
10188c2ecf20Sopenharmony_ci	.addr	= 0x60,
10198c2ecf20Sopenharmony_ci	.refclk	= 27000000,
10208c2ecf20Sopenharmony_ci	.clk_div = 1,
10218c2ecf20Sopenharmony_ci};
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_cistatic struct stv6110x_config tuner_cineS2_1 = {
10248c2ecf20Sopenharmony_ci	.addr	= 0x63,
10258c2ecf20Sopenharmony_ci	.refclk	= 27000000,
10268c2ecf20Sopenharmony_ci	.clk_div = 1,
10278c2ecf20Sopenharmony_ci};
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_cistatic const struct ngene_info ngene_info_cineS2 = {
10308c2ecf20Sopenharmony_ci	.type		= NGENE_SIDEWINDER,
10318c2ecf20Sopenharmony_ci	.name		= "Linux4Media cineS2 DVB-S2 Twin Tuner",
10328c2ecf20Sopenharmony_ci	.io_type	= {NGENE_IO_TSIN, NGENE_IO_TSIN},
10338c2ecf20Sopenharmony_ci	.demod_attach	= {demod_attach_stv0900, demod_attach_stv0900},
10348c2ecf20Sopenharmony_ci	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110},
10358c2ecf20Sopenharmony_ci	.fe_config	= {&fe_cineS2, &fe_cineS2},
10368c2ecf20Sopenharmony_ci	.tuner_config	= {&tuner_cineS2_0, &tuner_cineS2_1},
10378c2ecf20Sopenharmony_ci	.lnb		= {0x0b, 0x08},
10388c2ecf20Sopenharmony_ci	.tsf		= {3, 3},
10398c2ecf20Sopenharmony_ci	.fw_version	= 18,
10408c2ecf20Sopenharmony_ci	.msi_supported	= true,
10418c2ecf20Sopenharmony_ci};
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_cistatic const struct ngene_info ngene_info_satixS2 = {
10448c2ecf20Sopenharmony_ci	.type		= NGENE_SIDEWINDER,
10458c2ecf20Sopenharmony_ci	.name		= "Mystique SaTiX-S2 Dual",
10468c2ecf20Sopenharmony_ci	.io_type	= {NGENE_IO_TSIN, NGENE_IO_TSIN},
10478c2ecf20Sopenharmony_ci	.demod_attach	= {demod_attach_stv0900, demod_attach_stv0900},
10488c2ecf20Sopenharmony_ci	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110},
10498c2ecf20Sopenharmony_ci	.fe_config	= {&fe_cineS2, &fe_cineS2},
10508c2ecf20Sopenharmony_ci	.tuner_config	= {&tuner_cineS2_0, &tuner_cineS2_1},
10518c2ecf20Sopenharmony_ci	.lnb		= {0x0b, 0x08},
10528c2ecf20Sopenharmony_ci	.tsf		= {3, 3},
10538c2ecf20Sopenharmony_ci	.fw_version	= 18,
10548c2ecf20Sopenharmony_ci	.msi_supported	= true,
10558c2ecf20Sopenharmony_ci};
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_cistatic const struct ngene_info ngene_info_satixS2v2 = {
10588c2ecf20Sopenharmony_ci	.type		= NGENE_SIDEWINDER,
10598c2ecf20Sopenharmony_ci	.name		= "Mystique SaTiX-S2 Dual (v2)",
10608c2ecf20Sopenharmony_ci	.io_type	= {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
10618c2ecf20Sopenharmony_ci			   NGENE_IO_TSOUT},
10628c2ecf20Sopenharmony_ci	.demod_attach	= {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
10638c2ecf20Sopenharmony_ci	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
10648c2ecf20Sopenharmony_ci	.fe_config	= {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
10658c2ecf20Sopenharmony_ci	.tuner_config	= {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
10668c2ecf20Sopenharmony_ci	.lnb		= {0x0a, 0x08, 0x0b, 0x09},
10678c2ecf20Sopenharmony_ci	.tsf		= {3, 3},
10688c2ecf20Sopenharmony_ci	.fw_version	= 18,
10698c2ecf20Sopenharmony_ci	.msi_supported	= true,
10708c2ecf20Sopenharmony_ci};
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_cistatic const struct ngene_info ngene_info_cineS2v5 = {
10738c2ecf20Sopenharmony_ci	.type		= NGENE_SIDEWINDER,
10748c2ecf20Sopenharmony_ci	.name		= "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)",
10758c2ecf20Sopenharmony_ci	.io_type	= {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
10768c2ecf20Sopenharmony_ci			   NGENE_IO_TSOUT},
10778c2ecf20Sopenharmony_ci	.demod_attach	= {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
10788c2ecf20Sopenharmony_ci	.tuner_attach	= {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
10798c2ecf20Sopenharmony_ci	.fe_config	= {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
10808c2ecf20Sopenharmony_ci	.tuner_config	= {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
10818c2ecf20Sopenharmony_ci	.lnb		= {0x0a, 0x08, 0x0b, 0x09},
10828c2ecf20Sopenharmony_ci	.tsf		= {3, 3},
10838c2ecf20Sopenharmony_ci	.fw_version	= 18,
10848c2ecf20Sopenharmony_ci	.msi_supported	= true,
10858c2ecf20Sopenharmony_ci};
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_cistatic const struct ngene_info ngene_info_duoFlex = {
10898c2ecf20Sopenharmony_ci	.type           = NGENE_SIDEWINDER,
10908c2ecf20Sopenharmony_ci	.name           = "Digital Devices DuoFlex PCIe or miniPCIe",
10918c2ecf20Sopenharmony_ci	.io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
10928c2ecf20Sopenharmony_ci			   NGENE_IO_TSOUT},
10938c2ecf20Sopenharmony_ci	.demod_attach   = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe},
10948c2ecf20Sopenharmony_ci	.tuner_attach   = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe},
10958c2ecf20Sopenharmony_ci	.fe_config      = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
10968c2ecf20Sopenharmony_ci	.tuner_config   = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
10978c2ecf20Sopenharmony_ci	.lnb            = {0x0a, 0x08, 0x0b, 0x09},
10988c2ecf20Sopenharmony_ci	.tsf            = {3, 3},
10998c2ecf20Sopenharmony_ci	.fw_version     = 18,
11008c2ecf20Sopenharmony_ci	.msi_supported	= true,
11018c2ecf20Sopenharmony_ci};
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_cistatic const struct ngene_info ngene_info_m780 = {
11048c2ecf20Sopenharmony_ci	.type           = NGENE_APP,
11058c2ecf20Sopenharmony_ci	.name           = "Aver M780 ATSC/QAM-B",
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	/* Channel 0 is analog, which is currently unsupported */
11088c2ecf20Sopenharmony_ci	.io_type        = { NGENE_IO_NONE, NGENE_IO_TSIN },
11098c2ecf20Sopenharmony_ci	.demod_attach   = { NULL, demod_attach_lg330x },
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	/* Ensure these are NULL else the frame will call them (as funcs) */
11128c2ecf20Sopenharmony_ci	.tuner_attach   = { NULL, NULL, NULL, NULL },
11138c2ecf20Sopenharmony_ci	.fe_config      = { NULL, &aver_m780 },
11148c2ecf20Sopenharmony_ci	.avf            = { 0 },
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	/* A custom electrical interface config for the demod to bridge */
11178c2ecf20Sopenharmony_ci	.tsf		= { 4, 4 },
11188c2ecf20Sopenharmony_ci	.fw_version	= 15,
11198c2ecf20Sopenharmony_ci};
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_cistatic struct drxd_config fe_terratec_dvbt_0 = {
11228c2ecf20Sopenharmony_ci	.index          = 0,
11238c2ecf20Sopenharmony_ci	.demod_address  = 0x70,
11248c2ecf20Sopenharmony_ci	.demod_revision = 0xa2,
11258c2ecf20Sopenharmony_ci	.demoda_address = 0x00,
11268c2ecf20Sopenharmony_ci	.pll_address    = 0x60,
11278c2ecf20Sopenharmony_ci	.pll_type       = DVB_PLL_THOMSON_DTT7520X,
11288c2ecf20Sopenharmony_ci	.clock          = 20000,
11298c2ecf20Sopenharmony_ci	.osc_deviation  = osc_deviation,
11308c2ecf20Sopenharmony_ci};
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_cistatic struct drxd_config fe_terratec_dvbt_1 = {
11338c2ecf20Sopenharmony_ci	.index          = 1,
11348c2ecf20Sopenharmony_ci	.demod_address  = 0x71,
11358c2ecf20Sopenharmony_ci	.demod_revision = 0xa2,
11368c2ecf20Sopenharmony_ci	.demoda_address = 0x00,
11378c2ecf20Sopenharmony_ci	.pll_address    = 0x60,
11388c2ecf20Sopenharmony_ci	.pll_type       = DVB_PLL_THOMSON_DTT7520X,
11398c2ecf20Sopenharmony_ci	.clock          = 20000,
11408c2ecf20Sopenharmony_ci	.osc_deviation  = osc_deviation,
11418c2ecf20Sopenharmony_ci};
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic const struct ngene_info ngene_info_terratec = {
11448c2ecf20Sopenharmony_ci	.type           = NGENE_TERRATEC,
11458c2ecf20Sopenharmony_ci	.name           = "Terratec Integra/Cinergy2400i Dual DVB-T",
11468c2ecf20Sopenharmony_ci	.io_type        = {NGENE_IO_TSIN, NGENE_IO_TSIN},
11478c2ecf20Sopenharmony_ci	.demod_attach   = {demod_attach_drxd, demod_attach_drxd},
11488c2ecf20Sopenharmony_ci	.tuner_attach	= {tuner_attach_dtt7520x, tuner_attach_dtt7520x},
11498c2ecf20Sopenharmony_ci	.fe_config      = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1},
11508c2ecf20Sopenharmony_ci	.i2c_access     = 1,
11518c2ecf20Sopenharmony_ci};
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci/****************************************************************************/
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci/****************************************************************************/
11588c2ecf20Sopenharmony_ci/* PCI Subsystem ID *********************************************************/
11598c2ecf20Sopenharmony_ci/****************************************************************************/
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci#define NGENE_ID(_subvend, _subdev, _driverdata) { \
11628c2ecf20Sopenharmony_ci	.vendor = NGENE_VID, .device = NGENE_PID, \
11638c2ecf20Sopenharmony_ci	.subvendor = _subvend, .subdevice = _subdev, \
11648c2ecf20Sopenharmony_ci	.driver_data = (unsigned long) &_driverdata }
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci/****************************************************************************/
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_cistatic const struct pci_device_id ngene_id_tbl[] = {
11698c2ecf20Sopenharmony_ci	NGENE_ID(0x18c3, 0xab04, ngene_info_cineS2),
11708c2ecf20Sopenharmony_ci	NGENE_ID(0x18c3, 0xab05, ngene_info_cineS2v5),
11718c2ecf20Sopenharmony_ci	NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2),
11728c2ecf20Sopenharmony_ci	NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2),
11738c2ecf20Sopenharmony_ci	NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2),
11748c2ecf20Sopenharmony_ci	NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2),
11758c2ecf20Sopenharmony_ci	NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5),
11768c2ecf20Sopenharmony_ci	NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex),
11778c2ecf20Sopenharmony_ci	NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
11788c2ecf20Sopenharmony_ci	NGENE_ID(0x1461, 0x062e, ngene_info_m780),
11798c2ecf20Sopenharmony_ci	NGENE_ID(0x153b, 0x1167, ngene_info_terratec),
11808c2ecf20Sopenharmony_ci	{0}
11818c2ecf20Sopenharmony_ci};
11828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ngene_id_tbl);
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci/****************************************************************************/
11858c2ecf20Sopenharmony_ci/* Init/Exit ****************************************************************/
11868c2ecf20Sopenharmony_ci/****************************************************************************/
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_cistatic pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
11898c2ecf20Sopenharmony_ci					     pci_channel_state_t state)
11908c2ecf20Sopenharmony_ci{
11918c2ecf20Sopenharmony_ci	dev_err(&dev->dev, "PCI error\n");
11928c2ecf20Sopenharmony_ci	if (state == pci_channel_io_perm_failure)
11938c2ecf20Sopenharmony_ci		return PCI_ERS_RESULT_DISCONNECT;
11948c2ecf20Sopenharmony_ci	if (state == pci_channel_io_frozen)
11958c2ecf20Sopenharmony_ci		return PCI_ERS_RESULT_NEED_RESET;
11968c2ecf20Sopenharmony_ci	return PCI_ERS_RESULT_CAN_RECOVER;
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic pci_ers_result_t ngene_slot_reset(struct pci_dev *dev)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	dev_info(&dev->dev, "slot reset\n");
12028c2ecf20Sopenharmony_ci	return 0;
12038c2ecf20Sopenharmony_ci}
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_cistatic void ngene_resume(struct pci_dev *dev)
12068c2ecf20Sopenharmony_ci{
12078c2ecf20Sopenharmony_ci	dev_info(&dev->dev, "resume\n");
12088c2ecf20Sopenharmony_ci}
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_cistatic const struct pci_error_handlers ngene_errors = {
12118c2ecf20Sopenharmony_ci	.error_detected = ngene_error_detected,
12128c2ecf20Sopenharmony_ci	.slot_reset = ngene_slot_reset,
12138c2ecf20Sopenharmony_ci	.resume = ngene_resume,
12148c2ecf20Sopenharmony_ci};
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_cistatic struct pci_driver ngene_pci_driver = {
12178c2ecf20Sopenharmony_ci	.name        = "ngene",
12188c2ecf20Sopenharmony_ci	.id_table    = ngene_id_tbl,
12198c2ecf20Sopenharmony_ci	.probe       = ngene_probe,
12208c2ecf20Sopenharmony_ci	.remove      = ngene_remove,
12218c2ecf20Sopenharmony_ci	.err_handler = &ngene_errors,
12228c2ecf20Sopenharmony_ci	.shutdown    = ngene_shutdown,
12238c2ecf20Sopenharmony_ci};
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_cistatic __init int module_init_ngene(void)
12268c2ecf20Sopenharmony_ci{
12278c2ecf20Sopenharmony_ci	/* pr_*() since we don't have a device to use with dev_*() yet */
12288c2ecf20Sopenharmony_ci	pr_info("nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	return pci_register_driver(&ngene_pci_driver);
12318c2ecf20Sopenharmony_ci}
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_cistatic __exit void module_exit_ngene(void)
12348c2ecf20Sopenharmony_ci{
12358c2ecf20Sopenharmony_ci	pci_unregister_driver(&ngene_pci_driver);
12368c2ecf20Sopenharmony_ci}
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_cimodule_init(module_init_ngene);
12398c2ecf20Sopenharmony_cimodule_exit(module_exit_ngene);
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("nGene");
12428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel");
12438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1244