162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Driver for the NXP SAA7164 PCIe bridge
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "saa7164.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "tda10048.h"
1162306a36Sopenharmony_ci#include "tda18271.h"
1262306a36Sopenharmony_ci#include "s5h1411.h"
1362306a36Sopenharmony_ci#include "si2157.h"
1462306a36Sopenharmony_ci#include "si2168.h"
1562306a36Sopenharmony_ci#include "lgdt3306a.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define DRIVER_NAME "saa7164"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* addr is in the card struct, get it from there */
2262306a36Sopenharmony_cistatic struct tda10048_config hauppauge_hvr2200_1_config = {
2362306a36Sopenharmony_ci	.demod_address    = 0x10 >> 1,
2462306a36Sopenharmony_ci	.output_mode      = TDA10048_SERIAL_OUTPUT,
2562306a36Sopenharmony_ci	.fwbulkwritelen   = TDA10048_BULKWRITE_200,
2662306a36Sopenharmony_ci	.inversion        = TDA10048_INVERSION_ON,
2762306a36Sopenharmony_ci	.dtv6_if_freq_khz = TDA10048_IF_3300,
2862306a36Sopenharmony_ci	.dtv7_if_freq_khz = TDA10048_IF_3500,
2962306a36Sopenharmony_ci	.dtv8_if_freq_khz = TDA10048_IF_4000,
3062306a36Sopenharmony_ci	.clk_freq_khz     = TDA10048_CLK_16000,
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_cistatic struct tda10048_config hauppauge_hvr2200_2_config = {
3362306a36Sopenharmony_ci	.demod_address    = 0x12 >> 1,
3462306a36Sopenharmony_ci	.output_mode      = TDA10048_SERIAL_OUTPUT,
3562306a36Sopenharmony_ci	.fwbulkwritelen   = TDA10048_BULKWRITE_200,
3662306a36Sopenharmony_ci	.inversion        = TDA10048_INVERSION_ON,
3762306a36Sopenharmony_ci	.dtv6_if_freq_khz = TDA10048_IF_3300,
3862306a36Sopenharmony_ci	.dtv7_if_freq_khz = TDA10048_IF_3500,
3962306a36Sopenharmony_ci	.dtv8_if_freq_khz = TDA10048_IF_4000,
4062306a36Sopenharmony_ci	.clk_freq_khz     = TDA10048_CLK_16000,
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic struct tda18271_std_map hauppauge_tda18271_std_map = {
4462306a36Sopenharmony_ci	.atsc_6   = { .if_freq = 3250, .agc_mode = 3, .std = 3,
4562306a36Sopenharmony_ci		      .if_lvl = 6, .rfagc_top = 0x37 },
4662306a36Sopenharmony_ci	.qam_6    = { .if_freq = 4000, .agc_mode = 3, .std = 0,
4762306a36Sopenharmony_ci		      .if_lvl = 6, .rfagc_top = 0x37 },
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic struct tda18271_config hauppauge_hvr22x0_tuner_config = {
5162306a36Sopenharmony_ci	.std_map	= &hauppauge_tda18271_std_map,
5262306a36Sopenharmony_ci	.gate		= TDA18271_GATE_ANALOG,
5362306a36Sopenharmony_ci	.role		= TDA18271_MASTER,
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic struct tda18271_config hauppauge_hvr22x0s_tuner_config = {
5762306a36Sopenharmony_ci	.std_map	= &hauppauge_tda18271_std_map,
5862306a36Sopenharmony_ci	.gate		= TDA18271_GATE_ANALOG,
5962306a36Sopenharmony_ci	.role		= TDA18271_SLAVE,
6062306a36Sopenharmony_ci	.output_opt     = TDA18271_OUTPUT_LT_OFF,
6162306a36Sopenharmony_ci	.rf_cal_on_startup = 1
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic struct s5h1411_config hauppauge_s5h1411_config = {
6562306a36Sopenharmony_ci	.output_mode   = S5H1411_SERIAL_OUTPUT,
6662306a36Sopenharmony_ci	.gpio          = S5H1411_GPIO_ON,
6762306a36Sopenharmony_ci	.qam_if        = S5H1411_IF_4000,
6862306a36Sopenharmony_ci	.vsb_if        = S5H1411_IF_3250,
6962306a36Sopenharmony_ci	.inversion     = S5H1411_INVERSION_ON,
7062306a36Sopenharmony_ci	.status_mode   = S5H1411_DEMODLOCKING,
7162306a36Sopenharmony_ci	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic struct lgdt3306a_config hauppauge_hvr2255a_config = {
7562306a36Sopenharmony_ci	.i2c_addr               = 0xb2 >> 1,
7662306a36Sopenharmony_ci	.qam_if_khz             = 4000,
7762306a36Sopenharmony_ci	.vsb_if_khz             = 3250,
7862306a36Sopenharmony_ci	.deny_i2c_rptr          = 1, /* Disabled */
7962306a36Sopenharmony_ci	.spectral_inversion     = 0, /* Disabled */
8062306a36Sopenharmony_ci	.mpeg_mode              = LGDT3306A_MPEG_SERIAL,
8162306a36Sopenharmony_ci	.tpclk_edge             = LGDT3306A_TPCLK_RISING_EDGE,
8262306a36Sopenharmony_ci	.tpvalid_polarity       = LGDT3306A_TP_VALID_HIGH,
8362306a36Sopenharmony_ci	.xtalMHz                = 25, /* 24 or 25 */
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic struct lgdt3306a_config hauppauge_hvr2255b_config = {
8762306a36Sopenharmony_ci	.i2c_addr               = 0x1c >> 1,
8862306a36Sopenharmony_ci	.qam_if_khz             = 4000,
8962306a36Sopenharmony_ci	.vsb_if_khz             = 3250,
9062306a36Sopenharmony_ci	.deny_i2c_rptr          = 1, /* Disabled */
9162306a36Sopenharmony_ci	.spectral_inversion     = 0, /* Disabled */
9262306a36Sopenharmony_ci	.mpeg_mode              = LGDT3306A_MPEG_SERIAL,
9362306a36Sopenharmony_ci	.tpclk_edge             = LGDT3306A_TPCLK_RISING_EDGE,
9462306a36Sopenharmony_ci	.tpvalid_polarity       = LGDT3306A_TP_VALID_HIGH,
9562306a36Sopenharmony_ci	.xtalMHz                = 25, /* 24 or 25 */
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic struct si2157_config hauppauge_hvr2255_tuner_config = {
9962306a36Sopenharmony_ci	.inversion = 1,
10062306a36Sopenharmony_ci	.if_port = 1,
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic int si2157_attach(struct saa7164_port *port, struct i2c_adapter *adapter,
10462306a36Sopenharmony_ci	struct dvb_frontend *fe, u8 addr8bit, struct si2157_config *cfg)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct i2c_board_info bi;
10762306a36Sopenharmony_ci	struct i2c_client *tuner;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	cfg->fe = fe;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	memset(&bi, 0, sizeof(bi));
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	strscpy(bi.type, "si2157", I2C_NAME_SIZE);
11462306a36Sopenharmony_ci	bi.platform_data = cfg;
11562306a36Sopenharmony_ci	bi.addr = addr8bit >> 1;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	request_module(bi.type);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	tuner = i2c_new_client_device(adapter, &bi);
12062306a36Sopenharmony_ci	if (!i2c_client_has_driver(tuner))
12162306a36Sopenharmony_ci		return -ENODEV;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (!try_module_get(tuner->dev.driver->owner)) {
12462306a36Sopenharmony_ci		i2c_unregister_device(tuner);
12562306a36Sopenharmony_ci		return -ENODEV;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	port->i2c_client_tuner = tuner;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return 0;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int saa7164_dvb_stop_port(struct saa7164_port *port)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct saa7164_dev *dev = port->dev;
13662306a36Sopenharmony_ci	int ret;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
13962306a36Sopenharmony_ci	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
14062306a36Sopenharmony_ci		printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
14162306a36Sopenharmony_ci			__func__, ret);
14262306a36Sopenharmony_ci		ret = -EIO;
14362306a36Sopenharmony_ci	} else {
14462306a36Sopenharmony_ci		dprintk(DBGLVL_DVB, "%s()    Stopped\n", __func__);
14562306a36Sopenharmony_ci		ret = 0;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return ret;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int saa7164_dvb_acquire_port(struct saa7164_port *port)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct saa7164_dev *dev = port->dev;
15462306a36Sopenharmony_ci	int ret;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
15762306a36Sopenharmony_ci	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
15862306a36Sopenharmony_ci		printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
15962306a36Sopenharmony_ci			__func__, ret);
16062306a36Sopenharmony_ci		ret = -EIO;
16162306a36Sopenharmony_ci	} else {
16262306a36Sopenharmony_ci		dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__);
16362306a36Sopenharmony_ci		ret = 0;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return ret;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int saa7164_dvb_pause_port(struct saa7164_port *port)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct saa7164_dev *dev = port->dev;
17262306a36Sopenharmony_ci	int ret;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
17562306a36Sopenharmony_ci	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
17662306a36Sopenharmony_ci		printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
17762306a36Sopenharmony_ci			__func__, ret);
17862306a36Sopenharmony_ci		ret = -EIO;
17962306a36Sopenharmony_ci	} else {
18062306a36Sopenharmony_ci		dprintk(DBGLVL_DVB, "%s()   Paused\n", __func__);
18162306a36Sopenharmony_ci		ret = 0;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return ret;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/* Firmware is very windows centric, meaning you have to transition
18862306a36Sopenharmony_ci * the part through AVStream / KS Windows stages, forwards or backwards.
18962306a36Sopenharmony_ci * States are: stopped, acquired (h/w), paused, started.
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_cistatic int saa7164_dvb_stop_streaming(struct saa7164_port *port)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct saa7164_dev *dev = port->dev;
19462306a36Sopenharmony_ci	struct saa7164_buffer *buf;
19562306a36Sopenharmony_ci	struct list_head *p, *q;
19662306a36Sopenharmony_ci	int ret;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	ret = saa7164_dvb_pause_port(port);
20162306a36Sopenharmony_ci	ret = saa7164_dvb_acquire_port(port);
20262306a36Sopenharmony_ci	ret = saa7164_dvb_stop_port(port);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* Mark the hardware buffers as free */
20562306a36Sopenharmony_ci	mutex_lock(&port->dmaqueue_lock);
20662306a36Sopenharmony_ci	list_for_each_safe(p, q, &port->dmaqueue.list) {
20762306a36Sopenharmony_ci		buf = list_entry(p, struct saa7164_buffer, list);
20862306a36Sopenharmony_ci		buf->flags = SAA7164_BUFFER_FREE;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci	mutex_unlock(&port->dmaqueue_lock);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	return ret;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int saa7164_dvb_start_port(struct saa7164_port *port)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct saa7164_dev *dev = port->dev;
21862306a36Sopenharmony_ci	int ret = 0, result;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	saa7164_buffer_cfg_port(port);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* Acquire the hardware */
22562306a36Sopenharmony_ci	result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
22662306a36Sopenharmony_ci	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
22762306a36Sopenharmony_ci		printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
22862306a36Sopenharmony_ci			__func__, result);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		/* Stop the hardware, regardless */
23162306a36Sopenharmony_ci		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
23262306a36Sopenharmony_ci		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
23362306a36Sopenharmony_ci			printk(KERN_ERR "%s() acquire/forced stop transition failed, res = 0x%x\n",
23462306a36Sopenharmony_ci			       __func__, result);
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci		ret = -EIO;
23762306a36Sopenharmony_ci		goto out;
23862306a36Sopenharmony_ci	} else
23962306a36Sopenharmony_ci		dprintk(DBGLVL_DVB, "%s()   Acquired\n", __func__);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* Pause the hardware */
24262306a36Sopenharmony_ci	result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
24362306a36Sopenharmony_ci	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
24462306a36Sopenharmony_ci		printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
24562306a36Sopenharmony_ci				__func__, result);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		/* Stop the hardware, regardless */
24862306a36Sopenharmony_ci		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
24962306a36Sopenharmony_ci		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
25062306a36Sopenharmony_ci			printk(KERN_ERR "%s() pause/forced stop transition failed, res = 0x%x\n",
25162306a36Sopenharmony_ci			       __func__, result);
25262306a36Sopenharmony_ci		}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		ret = -EIO;
25562306a36Sopenharmony_ci		goto out;
25662306a36Sopenharmony_ci	} else
25762306a36Sopenharmony_ci		dprintk(DBGLVL_DVB, "%s()   Paused\n", __func__);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* Start the hardware */
26062306a36Sopenharmony_ci	result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
26162306a36Sopenharmony_ci	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
26262306a36Sopenharmony_ci		printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
26362306a36Sopenharmony_ci				__func__, result);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci		/* Stop the hardware, regardless */
26662306a36Sopenharmony_ci		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
26762306a36Sopenharmony_ci		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
26862306a36Sopenharmony_ci			printk(KERN_ERR "%s() run/forced stop transition failed, res = 0x%x\n",
26962306a36Sopenharmony_ci			       __func__, result);
27062306a36Sopenharmony_ci		}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		ret = -EIO;
27362306a36Sopenharmony_ci	} else
27462306a36Sopenharmony_ci		dprintk(DBGLVL_DVB, "%s()   Running\n", __func__);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ciout:
27762306a36Sopenharmony_ci	return ret;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int saa7164_dvb_start_feed(struct dvb_demux_feed *feed)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct dvb_demux *demux = feed->demux;
28362306a36Sopenharmony_ci	struct saa7164_port *port = demux->priv;
28462306a36Sopenharmony_ci	struct saa7164_dvb *dvb = &port->dvb;
28562306a36Sopenharmony_ci	struct saa7164_dev *dev = port->dev;
28662306a36Sopenharmony_ci	int ret = 0;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (!demux->dmx.frontend)
29162306a36Sopenharmony_ci		return -EINVAL;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (dvb) {
29462306a36Sopenharmony_ci		mutex_lock(&dvb->lock);
29562306a36Sopenharmony_ci		if (dvb->feeding++ == 0) {
29662306a36Sopenharmony_ci			/* Start transport */
29762306a36Sopenharmony_ci			ret = saa7164_dvb_start_port(port);
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci		mutex_unlock(&dvb->lock);
30062306a36Sopenharmony_ci		dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n",
30162306a36Sopenharmony_ci			__func__, port->nr, dvb->feeding);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return ret;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct dvb_demux *demux = feed->demux;
31062306a36Sopenharmony_ci	struct saa7164_port *port = demux->priv;
31162306a36Sopenharmony_ci	struct saa7164_dvb *dvb = &port->dvb;
31262306a36Sopenharmony_ci	struct saa7164_dev *dev = port->dev;
31362306a36Sopenharmony_ci	int ret = 0;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (dvb) {
31862306a36Sopenharmony_ci		mutex_lock(&dvb->lock);
31962306a36Sopenharmony_ci		if (--dvb->feeding == 0) {
32062306a36Sopenharmony_ci			/* Stop transport */
32162306a36Sopenharmony_ci			ret = saa7164_dvb_stop_streaming(port);
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci		mutex_unlock(&dvb->lock);
32462306a36Sopenharmony_ci		dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n",
32562306a36Sopenharmony_ci			__func__, port->nr, dvb->feeding);
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return ret;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int dvb_register(struct saa7164_port *port)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct saa7164_dvb *dvb = &port->dvb;
33462306a36Sopenharmony_ci	struct saa7164_dev *dev = port->dev;
33562306a36Sopenharmony_ci	struct saa7164_buffer *buf;
33662306a36Sopenharmony_ci	int result, i;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	BUG_ON(port->type != SAA7164_MPEG_DVB);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Sanity check that the PCI configuration space is active */
34362306a36Sopenharmony_ci	if (port->hwcfg.BARLocation == 0) {
34462306a36Sopenharmony_ci		result = -ENOMEM;
34562306a36Sopenharmony_ci		printk(KERN_ERR "%s: dvb_register_adapter failed (errno = %d), NO PCI configuration\n",
34662306a36Sopenharmony_ci			DRIVER_NAME, result);
34762306a36Sopenharmony_ci		goto fail_adapter;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* Init and establish defaults */
35162306a36Sopenharmony_ci	port->hw_streamingparams.bitspersample = 8;
35262306a36Sopenharmony_ci	port->hw_streamingparams.samplesperline = 188;
35362306a36Sopenharmony_ci	port->hw_streamingparams.numberoflines =
35462306a36Sopenharmony_ci		(SAA7164_TS_NUMBER_OF_LINES * 188) / 188;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	port->hw_streamingparams.pitch = 188;
35762306a36Sopenharmony_ci	port->hw_streamingparams.linethreshold = 0;
35862306a36Sopenharmony_ci	port->hw_streamingparams.pagetablelistvirt = NULL;
35962306a36Sopenharmony_ci	port->hw_streamingparams.pagetablelistphys = NULL;
36062306a36Sopenharmony_ci	port->hw_streamingparams.numpagetables = 2 +
36162306a36Sopenharmony_ci		((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	port->hw_streamingparams.numpagetableentries = port->hwcfg.buffercount;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/* Allocate the PCI resources */
36662306a36Sopenharmony_ci	for (i = 0; i < port->hwcfg.buffercount; i++) {
36762306a36Sopenharmony_ci		buf = saa7164_buffer_alloc(port,
36862306a36Sopenharmony_ci			port->hw_streamingparams.numberoflines *
36962306a36Sopenharmony_ci			port->hw_streamingparams.pitch);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		if (!buf) {
37262306a36Sopenharmony_ci			result = -ENOMEM;
37362306a36Sopenharmony_ci			printk(KERN_ERR "%s: dvb_register_adapter failed (errno = %d), unable to allocate buffers\n",
37462306a36Sopenharmony_ci				DRIVER_NAME, result);
37562306a36Sopenharmony_ci			goto fail_adapter;
37662306a36Sopenharmony_ci		}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		mutex_lock(&port->dmaqueue_lock);
37962306a36Sopenharmony_ci		list_add_tail(&buf->list, &port->dmaqueue.list);
38062306a36Sopenharmony_ci		mutex_unlock(&port->dmaqueue_lock);
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* register adapter */
38462306a36Sopenharmony_ci	result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE,
38562306a36Sopenharmony_ci			&dev->pci->dev, adapter_nr);
38662306a36Sopenharmony_ci	if (result < 0) {
38762306a36Sopenharmony_ci		printk(KERN_ERR "%s: dvb_register_adapter failed (errno = %d)\n",
38862306a36Sopenharmony_ci		       DRIVER_NAME, result);
38962306a36Sopenharmony_ci		goto fail_adapter;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci	dvb->adapter.priv = port;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* register frontend */
39462306a36Sopenharmony_ci	result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
39562306a36Sopenharmony_ci	if (result < 0) {
39662306a36Sopenharmony_ci		printk(KERN_ERR "%s: dvb_register_frontend failed (errno = %d)\n",
39762306a36Sopenharmony_ci		       DRIVER_NAME, result);
39862306a36Sopenharmony_ci		goto fail_frontend;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* register demux stuff */
40262306a36Sopenharmony_ci	dvb->demux.dmx.capabilities =
40362306a36Sopenharmony_ci		DMX_TS_FILTERING | DMX_SECTION_FILTERING |
40462306a36Sopenharmony_ci		DMX_MEMORY_BASED_FILTERING;
40562306a36Sopenharmony_ci	dvb->demux.priv       = port;
40662306a36Sopenharmony_ci	dvb->demux.filternum  = 256;
40762306a36Sopenharmony_ci	dvb->demux.feednum    = 256;
40862306a36Sopenharmony_ci	dvb->demux.start_feed = saa7164_dvb_start_feed;
40962306a36Sopenharmony_ci	dvb->demux.stop_feed  = saa7164_dvb_stop_feed;
41062306a36Sopenharmony_ci	result = dvb_dmx_init(&dvb->demux);
41162306a36Sopenharmony_ci	if (result < 0) {
41262306a36Sopenharmony_ci		printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n",
41362306a36Sopenharmony_ci		       DRIVER_NAME, result);
41462306a36Sopenharmony_ci		goto fail_dmx;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	dvb->dmxdev.filternum    = 256;
41862306a36Sopenharmony_ci	dvb->dmxdev.demux        = &dvb->demux.dmx;
41962306a36Sopenharmony_ci	dvb->dmxdev.capabilities = 0;
42062306a36Sopenharmony_ci	result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
42162306a36Sopenharmony_ci	if (result < 0) {
42262306a36Sopenharmony_ci		printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n",
42362306a36Sopenharmony_ci		       DRIVER_NAME, result);
42462306a36Sopenharmony_ci		goto fail_dmxdev;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	dvb->fe_hw.source = DMX_FRONTEND_0;
42862306a36Sopenharmony_ci	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
42962306a36Sopenharmony_ci	if (result < 0) {
43062306a36Sopenharmony_ci		printk(KERN_ERR "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
43162306a36Sopenharmony_ci		       DRIVER_NAME, result);
43262306a36Sopenharmony_ci		goto fail_fe_hw;
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	dvb->fe_mem.source = DMX_MEMORY_FE;
43662306a36Sopenharmony_ci	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
43762306a36Sopenharmony_ci	if (result < 0) {
43862306a36Sopenharmony_ci		printk(KERN_ERR "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
43962306a36Sopenharmony_ci		       DRIVER_NAME, result);
44062306a36Sopenharmony_ci		goto fail_fe_mem;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
44462306a36Sopenharmony_ci	if (result < 0) {
44562306a36Sopenharmony_ci		printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n",
44662306a36Sopenharmony_ci		       DRIVER_NAME, result);
44762306a36Sopenharmony_ci		goto fail_fe_conn;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* register network adapter */
45162306a36Sopenharmony_ci	dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cifail_fe_conn:
45562306a36Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
45662306a36Sopenharmony_cifail_fe_mem:
45762306a36Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
45862306a36Sopenharmony_cifail_fe_hw:
45962306a36Sopenharmony_ci	dvb_dmxdev_release(&dvb->dmxdev);
46062306a36Sopenharmony_cifail_dmxdev:
46162306a36Sopenharmony_ci	dvb_dmx_release(&dvb->demux);
46262306a36Sopenharmony_cifail_dmx:
46362306a36Sopenharmony_ci	dvb_unregister_frontend(dvb->frontend);
46462306a36Sopenharmony_cifail_frontend:
46562306a36Sopenharmony_ci	dvb_frontend_detach(dvb->frontend);
46662306a36Sopenharmony_ci	dvb_unregister_adapter(&dvb->adapter);
46762306a36Sopenharmony_cifail_adapter:
46862306a36Sopenharmony_ci	return result;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ciint saa7164_dvb_unregister(struct saa7164_port *port)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct saa7164_dvb *dvb = &port->dvb;
47462306a36Sopenharmony_ci	struct saa7164_dev *dev = port->dev;
47562306a36Sopenharmony_ci	struct saa7164_buffer *b;
47662306a36Sopenharmony_ci	struct list_head *c, *n;
47762306a36Sopenharmony_ci	struct i2c_client *client;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	dprintk(DBGLVL_DVB, "%s()\n", __func__);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	BUG_ON(port->type != SAA7164_MPEG_DVB);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* Remove any allocated buffers */
48462306a36Sopenharmony_ci	mutex_lock(&port->dmaqueue_lock);
48562306a36Sopenharmony_ci	list_for_each_safe(c, n, &port->dmaqueue.list) {
48662306a36Sopenharmony_ci		b = list_entry(c, struct saa7164_buffer, list);
48762306a36Sopenharmony_ci		list_del(c);
48862306a36Sopenharmony_ci		saa7164_buffer_dealloc(b);
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci	mutex_unlock(&port->dmaqueue_lock);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (dvb->frontend == NULL)
49362306a36Sopenharmony_ci		return 0;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* remove I2C client for tuner */
49662306a36Sopenharmony_ci	client = port->i2c_client_tuner;
49762306a36Sopenharmony_ci	if (client) {
49862306a36Sopenharmony_ci		module_put(client->dev.driver->owner);
49962306a36Sopenharmony_ci		i2c_unregister_device(client);
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* remove I2C client for demodulator */
50362306a36Sopenharmony_ci	client = port->i2c_client_demod;
50462306a36Sopenharmony_ci	if (client) {
50562306a36Sopenharmony_ci		module_put(client->dev.driver->owner);
50662306a36Sopenharmony_ci		i2c_unregister_device(client);
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	dvb_net_release(&dvb->net);
51062306a36Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
51162306a36Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
51262306a36Sopenharmony_ci	dvb_dmxdev_release(&dvb->dmxdev);
51362306a36Sopenharmony_ci	dvb_dmx_release(&dvb->demux);
51462306a36Sopenharmony_ci	dvb_unregister_frontend(dvb->frontend);
51562306a36Sopenharmony_ci	dvb_frontend_detach(dvb->frontend);
51662306a36Sopenharmony_ci	dvb_unregister_adapter(&dvb->adapter);
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci/* All the DVB attach calls go here, this function gets modified
52162306a36Sopenharmony_ci * for each new card.
52262306a36Sopenharmony_ci */
52362306a36Sopenharmony_ciint saa7164_dvb_register(struct saa7164_port *port)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct saa7164_dev *dev = port->dev;
52662306a36Sopenharmony_ci	struct saa7164_dvb *dvb = &port->dvb;
52762306a36Sopenharmony_ci	struct saa7164_i2c *i2c_bus = NULL;
52862306a36Sopenharmony_ci	struct si2168_config si2168_config;
52962306a36Sopenharmony_ci	struct si2157_config si2157_config;
53062306a36Sopenharmony_ci	struct i2c_adapter *adapter;
53162306a36Sopenharmony_ci	struct i2c_board_info info;
53262306a36Sopenharmony_ci	struct i2c_client *client_demod;
53362306a36Sopenharmony_ci	struct i2c_client *client_tuner;
53462306a36Sopenharmony_ci	int ret;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	dprintk(DBGLVL_DVB, "%s()\n", __func__);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	/* init frontend */
53962306a36Sopenharmony_ci	switch (dev->board) {
54062306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2200:
54162306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2200_2:
54262306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2200_3:
54362306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2200_4:
54462306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2200_5:
54562306a36Sopenharmony_ci		i2c_bus = &dev->i2c_bus[port->nr + 1];
54662306a36Sopenharmony_ci		switch (port->nr) {
54762306a36Sopenharmony_ci		case 0:
54862306a36Sopenharmony_ci			port->dvb.frontend = dvb_attach(tda10048_attach,
54962306a36Sopenharmony_ci				&hauppauge_hvr2200_1_config,
55062306a36Sopenharmony_ci				&i2c_bus->i2c_adap);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci			if (port->dvb.frontend != NULL) {
55362306a36Sopenharmony_ci				/* TODO: addr is in the card struct */
55462306a36Sopenharmony_ci				dvb_attach(tda18271_attach, port->dvb.frontend,
55562306a36Sopenharmony_ci					0xc0 >> 1, &i2c_bus->i2c_adap,
55662306a36Sopenharmony_ci					&hauppauge_hvr22x0_tuner_config);
55762306a36Sopenharmony_ci			}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci			break;
56062306a36Sopenharmony_ci		case 1:
56162306a36Sopenharmony_ci			port->dvb.frontend = dvb_attach(tda10048_attach,
56262306a36Sopenharmony_ci				&hauppauge_hvr2200_2_config,
56362306a36Sopenharmony_ci				&i2c_bus->i2c_adap);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci			if (port->dvb.frontend != NULL) {
56662306a36Sopenharmony_ci				/* TODO: addr is in the card struct */
56762306a36Sopenharmony_ci				dvb_attach(tda18271_attach, port->dvb.frontend,
56862306a36Sopenharmony_ci					0xc0 >> 1, &i2c_bus->i2c_adap,
56962306a36Sopenharmony_ci					&hauppauge_hvr22x0s_tuner_config);
57062306a36Sopenharmony_ci			}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci			break;
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci		break;
57562306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2250:
57662306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2250_2:
57762306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2250_3:
57862306a36Sopenharmony_ci		i2c_bus = &dev->i2c_bus[port->nr + 1];
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		port->dvb.frontend = dvb_attach(s5h1411_attach,
58162306a36Sopenharmony_ci			&hauppauge_s5h1411_config,
58262306a36Sopenharmony_ci			&i2c_bus->i2c_adap);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		if (port->dvb.frontend != NULL) {
58562306a36Sopenharmony_ci			if (port->nr == 0) {
58662306a36Sopenharmony_ci				/* Master TDA18271 */
58762306a36Sopenharmony_ci				/* TODO: addr is in the card struct */
58862306a36Sopenharmony_ci				dvb_attach(tda18271_attach, port->dvb.frontend,
58962306a36Sopenharmony_ci					0xc0 >> 1, &i2c_bus->i2c_adap,
59062306a36Sopenharmony_ci					&hauppauge_hvr22x0_tuner_config);
59162306a36Sopenharmony_ci			} else {
59262306a36Sopenharmony_ci				/* Slave TDA18271 */
59362306a36Sopenharmony_ci				dvb_attach(tda18271_attach, port->dvb.frontend,
59462306a36Sopenharmony_ci					0xc0 >> 1, &i2c_bus->i2c_adap,
59562306a36Sopenharmony_ci					&hauppauge_hvr22x0s_tuner_config);
59662306a36Sopenharmony_ci			}
59762306a36Sopenharmony_ci		}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		break;
60062306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2255proto:
60162306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2255:
60262306a36Sopenharmony_ci		i2c_bus = &dev->i2c_bus[2];
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		if (port->nr == 0) {
60562306a36Sopenharmony_ci			port->dvb.frontend = dvb_attach(lgdt3306a_attach,
60662306a36Sopenharmony_ci				&hauppauge_hvr2255a_config, &i2c_bus->i2c_adap);
60762306a36Sopenharmony_ci		} else {
60862306a36Sopenharmony_ci			port->dvb.frontend = dvb_attach(lgdt3306a_attach,
60962306a36Sopenharmony_ci				&hauppauge_hvr2255b_config, &i2c_bus->i2c_adap);
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		if (port->dvb.frontend != NULL) {
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci			if (port->nr == 0) {
61562306a36Sopenharmony_ci				si2157_attach(port, &dev->i2c_bus[0].i2c_adap,
61662306a36Sopenharmony_ci					      port->dvb.frontend, 0xc0,
61762306a36Sopenharmony_ci					      &hauppauge_hvr2255_tuner_config);
61862306a36Sopenharmony_ci			} else {
61962306a36Sopenharmony_ci				si2157_attach(port, &dev->i2c_bus[1].i2c_adap,
62062306a36Sopenharmony_ci					      port->dvb.frontend, 0xc0,
62162306a36Sopenharmony_ci					      &hauppauge_hvr2255_tuner_config);
62262306a36Sopenharmony_ci			}
62362306a36Sopenharmony_ci		}
62462306a36Sopenharmony_ci		break;
62562306a36Sopenharmony_ci	case SAA7164_BOARD_HAUPPAUGE_HVR2205:
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		if (port->nr == 0) {
62862306a36Sopenharmony_ci			/* attach frontend */
62962306a36Sopenharmony_ci			memset(&si2168_config, 0, sizeof(si2168_config));
63062306a36Sopenharmony_ci			si2168_config.i2c_adapter = &adapter;
63162306a36Sopenharmony_ci			si2168_config.fe = &port->dvb.frontend;
63262306a36Sopenharmony_ci			si2168_config.ts_mode = SI2168_TS_SERIAL;
63362306a36Sopenharmony_ci			memset(&info, 0, sizeof(struct i2c_board_info));
63462306a36Sopenharmony_ci			strscpy(info.type, "si2168", I2C_NAME_SIZE);
63562306a36Sopenharmony_ci			info.addr = 0xc8 >> 1;
63662306a36Sopenharmony_ci			info.platform_data = &si2168_config;
63762306a36Sopenharmony_ci			request_module(info.type);
63862306a36Sopenharmony_ci			client_demod = i2c_new_client_device(&dev->i2c_bus[2].i2c_adap, &info);
63962306a36Sopenharmony_ci			if (!i2c_client_has_driver(client_demod))
64062306a36Sopenharmony_ci				goto frontend_detach;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci			if (!try_module_get(client_demod->dev.driver->owner)) {
64362306a36Sopenharmony_ci				i2c_unregister_device(client_demod);
64462306a36Sopenharmony_ci				goto frontend_detach;
64562306a36Sopenharmony_ci			}
64662306a36Sopenharmony_ci			port->i2c_client_demod = client_demod;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci			/* attach tuner */
64962306a36Sopenharmony_ci			memset(&si2157_config, 0, sizeof(si2157_config));
65062306a36Sopenharmony_ci			si2157_config.if_port = 1;
65162306a36Sopenharmony_ci			si2157_config.fe = port->dvb.frontend;
65262306a36Sopenharmony_ci			memset(&info, 0, sizeof(struct i2c_board_info));
65362306a36Sopenharmony_ci			strscpy(info.type, "si2157", I2C_NAME_SIZE);
65462306a36Sopenharmony_ci			info.addr = 0xc0 >> 1;
65562306a36Sopenharmony_ci			info.platform_data = &si2157_config;
65662306a36Sopenharmony_ci			request_module(info.type);
65762306a36Sopenharmony_ci			client_tuner = i2c_new_client_device(&dev->i2c_bus[0].i2c_adap, &info);
65862306a36Sopenharmony_ci			if (!i2c_client_has_driver(client_tuner)) {
65962306a36Sopenharmony_ci				module_put(client_demod->dev.driver->owner);
66062306a36Sopenharmony_ci				i2c_unregister_device(client_demod);
66162306a36Sopenharmony_ci				goto frontend_detach;
66262306a36Sopenharmony_ci			}
66362306a36Sopenharmony_ci			if (!try_module_get(client_tuner->dev.driver->owner)) {
66462306a36Sopenharmony_ci				i2c_unregister_device(client_tuner);
66562306a36Sopenharmony_ci				module_put(client_demod->dev.driver->owner);
66662306a36Sopenharmony_ci				i2c_unregister_device(client_demod);
66762306a36Sopenharmony_ci				goto frontend_detach;
66862306a36Sopenharmony_ci			}
66962306a36Sopenharmony_ci			port->i2c_client_tuner = client_tuner;
67062306a36Sopenharmony_ci		} else {
67162306a36Sopenharmony_ci			/* attach frontend */
67262306a36Sopenharmony_ci			memset(&si2168_config, 0, sizeof(si2168_config));
67362306a36Sopenharmony_ci			si2168_config.i2c_adapter = &adapter;
67462306a36Sopenharmony_ci			si2168_config.fe = &port->dvb.frontend;
67562306a36Sopenharmony_ci			si2168_config.ts_mode = SI2168_TS_SERIAL;
67662306a36Sopenharmony_ci			memset(&info, 0, sizeof(struct i2c_board_info));
67762306a36Sopenharmony_ci			strscpy(info.type, "si2168", I2C_NAME_SIZE);
67862306a36Sopenharmony_ci			info.addr = 0xcc >> 1;
67962306a36Sopenharmony_ci			info.platform_data = &si2168_config;
68062306a36Sopenharmony_ci			request_module(info.type);
68162306a36Sopenharmony_ci			client_demod = i2c_new_client_device(&dev->i2c_bus[2].i2c_adap, &info);
68262306a36Sopenharmony_ci			if (!i2c_client_has_driver(client_demod))
68362306a36Sopenharmony_ci				goto frontend_detach;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci			if (!try_module_get(client_demod->dev.driver->owner)) {
68662306a36Sopenharmony_ci				i2c_unregister_device(client_demod);
68762306a36Sopenharmony_ci				goto frontend_detach;
68862306a36Sopenharmony_ci			}
68962306a36Sopenharmony_ci			port->i2c_client_demod = client_demod;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci			/* attach tuner */
69262306a36Sopenharmony_ci			memset(&si2157_config, 0, sizeof(si2157_config));
69362306a36Sopenharmony_ci			si2157_config.fe = port->dvb.frontend;
69462306a36Sopenharmony_ci			si2157_config.if_port = 1;
69562306a36Sopenharmony_ci			memset(&info, 0, sizeof(struct i2c_board_info));
69662306a36Sopenharmony_ci			strscpy(info.type, "si2157", I2C_NAME_SIZE);
69762306a36Sopenharmony_ci			info.addr = 0xc0 >> 1;
69862306a36Sopenharmony_ci			info.platform_data = &si2157_config;
69962306a36Sopenharmony_ci			request_module(info.type);
70062306a36Sopenharmony_ci			client_tuner = i2c_new_client_device(&dev->i2c_bus[1].i2c_adap, &info);
70162306a36Sopenharmony_ci			if (!i2c_client_has_driver(client_tuner)) {
70262306a36Sopenharmony_ci				module_put(client_demod->dev.driver->owner);
70362306a36Sopenharmony_ci				i2c_unregister_device(client_demod);
70462306a36Sopenharmony_ci				goto frontend_detach;
70562306a36Sopenharmony_ci			}
70662306a36Sopenharmony_ci			if (!try_module_get(client_tuner->dev.driver->owner)) {
70762306a36Sopenharmony_ci				i2c_unregister_device(client_tuner);
70862306a36Sopenharmony_ci				module_put(client_demod->dev.driver->owner);
70962306a36Sopenharmony_ci				i2c_unregister_device(client_demod);
71062306a36Sopenharmony_ci				goto frontend_detach;
71162306a36Sopenharmony_ci			}
71262306a36Sopenharmony_ci			port->i2c_client_tuner = client_tuner;
71362306a36Sopenharmony_ci		}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		break;
71662306a36Sopenharmony_ci	default:
71762306a36Sopenharmony_ci		printk(KERN_ERR "%s: The frontend isn't supported\n",
71862306a36Sopenharmony_ci		       dev->name);
71962306a36Sopenharmony_ci		break;
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci	if (NULL == dvb->frontend) {
72262306a36Sopenharmony_ci		printk(KERN_ERR "%s() Frontend initialization failed\n",
72362306a36Sopenharmony_ci		       __func__);
72462306a36Sopenharmony_ci		return -1;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/* register everything */
72862306a36Sopenharmony_ci	ret = dvb_register(port);
72962306a36Sopenharmony_ci	if (ret < 0) {
73062306a36Sopenharmony_ci		if (dvb->frontend->ops.release)
73162306a36Sopenharmony_ci			dvb->frontend->ops.release(dvb->frontend);
73262306a36Sopenharmony_ci		return ret;
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	return 0;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cifrontend_detach:
73862306a36Sopenharmony_ci	printk(KERN_ERR "%s() Frontend/I2C initialization failed\n", __func__);
73962306a36Sopenharmony_ci	return -1;
74062306a36Sopenharmony_ci}
741