18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci DVB device driver for cx231xx
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
68c2ecf20Sopenharmony_ci		Based on em28xx driver
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "cx231xx.h"
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <media/dvbdev.h>
158c2ecf20Sopenharmony_ci#include <media/dmxdev.h>
168c2ecf20Sopenharmony_ci#include <media/dvb_demux.h>
178c2ecf20Sopenharmony_ci#include <media/dvb_net.h>
188c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
198c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
208c2ecf20Sopenharmony_ci#include <media/tuner.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "xc5000.h"
238c2ecf20Sopenharmony_ci#include "s5h1432.h"
248c2ecf20Sopenharmony_ci#include "tda18271.h"
258c2ecf20Sopenharmony_ci#include "s5h1411.h"
268c2ecf20Sopenharmony_ci#include "lgdt3305.h"
278c2ecf20Sopenharmony_ci#include "si2165.h"
288c2ecf20Sopenharmony_ci#include "si2168.h"
298c2ecf20Sopenharmony_ci#include "mb86a20s.h"
308c2ecf20Sopenharmony_ci#include "si2157.h"
318c2ecf20Sopenharmony_ci#include "lgdt3306a.h"
328c2ecf20Sopenharmony_ci#include "r820t.h"
338c2ecf20Sopenharmony_ci#include "mn88473.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("driver for cx231xx based DVB cards");
368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic unsigned int debug;
408c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "enable debug messages [dvb]");
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define CX231XX_DVB_NUM_BUFS 5
468c2ecf20Sopenharmony_ci#define CX231XX_DVB_MAX_PACKETSIZE 564
478c2ecf20Sopenharmony_ci#define CX231XX_DVB_MAX_PACKETS 64
488c2ecf20Sopenharmony_ci#define CX231XX_DVB_MAX_FRONTENDS 2
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct cx231xx_dvb {
518c2ecf20Sopenharmony_ci	struct dvb_frontend *frontend[CX231XX_DVB_MAX_FRONTENDS];
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	/* feed count management */
548c2ecf20Sopenharmony_ci	struct mutex lock;
558c2ecf20Sopenharmony_ci	int nfeeds;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* general boilerplate stuff */
588c2ecf20Sopenharmony_ci	struct dvb_adapter adapter;
598c2ecf20Sopenharmony_ci	struct dvb_demux demux;
608c2ecf20Sopenharmony_ci	struct dmxdev dmxdev;
618c2ecf20Sopenharmony_ci	struct dmx_frontend fe_hw;
628c2ecf20Sopenharmony_ci	struct dmx_frontend fe_mem;
638c2ecf20Sopenharmony_ci	struct dvb_net net;
648c2ecf20Sopenharmony_ci	struct i2c_client *i2c_client_demod[2];
658c2ecf20Sopenharmony_ci	struct i2c_client *i2c_client_tuner;
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic struct s5h1432_config dvico_s5h1432_config = {
698c2ecf20Sopenharmony_ci	.output_mode   = S5H1432_SERIAL_OUTPUT,
708c2ecf20Sopenharmony_ci	.gpio          = S5H1432_GPIO_ON,
718c2ecf20Sopenharmony_ci	.qam_if        = S5H1432_IF_4000,
728c2ecf20Sopenharmony_ci	.vsb_if        = S5H1432_IF_4000,
738c2ecf20Sopenharmony_ci	.inversion     = S5H1432_INVERSION_OFF,
748c2ecf20Sopenharmony_ci	.status_mode   = S5H1432_DEMODLOCKING,
758c2ecf20Sopenharmony_ci	.mpeg_timing   = S5H1432_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic struct tda18271_std_map cnxt_rde253s_tda18271_std_map = {
798c2ecf20Sopenharmony_ci	.dvbt_6   = { .if_freq = 4000, .agc_mode = 3, .std = 4,
808c2ecf20Sopenharmony_ci		      .if_lvl = 1, .rfagc_top = 0x37, },
818c2ecf20Sopenharmony_ci	.dvbt_7   = { .if_freq = 4000, .agc_mode = 3, .std = 5,
828c2ecf20Sopenharmony_ci		      .if_lvl = 1, .rfagc_top = 0x37, },
838c2ecf20Sopenharmony_ci	.dvbt_8   = { .if_freq = 4000, .agc_mode = 3, .std = 6,
848c2ecf20Sopenharmony_ci		      .if_lvl = 1, .rfagc_top = 0x37, },
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic struct tda18271_std_map mb86a20s_tda18271_config = {
888c2ecf20Sopenharmony_ci	.dvbt_6   = { .if_freq = 4000, .agc_mode = 3, .std = 4,
898c2ecf20Sopenharmony_ci		      .if_lvl = 0, .rfagc_top = 0x37, },
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic struct tda18271_config cnxt_rde253s_tunerconfig = {
938c2ecf20Sopenharmony_ci	.std_map = &cnxt_rde253s_tda18271_std_map,
948c2ecf20Sopenharmony_ci	.gate    = TDA18271_GATE_ANALOG,
958c2ecf20Sopenharmony_ci};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic struct s5h1411_config tda18271_s5h1411_config = {
988c2ecf20Sopenharmony_ci	.output_mode   = S5H1411_SERIAL_OUTPUT,
998c2ecf20Sopenharmony_ci	.gpio          = S5H1411_GPIO_OFF,
1008c2ecf20Sopenharmony_ci	.vsb_if        = S5H1411_IF_3250,
1018c2ecf20Sopenharmony_ci	.qam_if        = S5H1411_IF_4000,
1028c2ecf20Sopenharmony_ci	.inversion     = S5H1411_INVERSION_ON,
1038c2ecf20Sopenharmony_ci	.status_mode   = S5H1411_DEMODLOCKING,
1048c2ecf20Sopenharmony_ci	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_cistatic struct s5h1411_config xc5000_s5h1411_config = {
1078c2ecf20Sopenharmony_ci	.output_mode   = S5H1411_SERIAL_OUTPUT,
1088c2ecf20Sopenharmony_ci	.gpio          = S5H1411_GPIO_OFF,
1098c2ecf20Sopenharmony_ci	.vsb_if        = S5H1411_IF_3250,
1108c2ecf20Sopenharmony_ci	.qam_if        = S5H1411_IF_3250,
1118c2ecf20Sopenharmony_ci	.inversion     = S5H1411_INVERSION_OFF,
1128c2ecf20Sopenharmony_ci	.status_mode   = S5H1411_DEMODLOCKING,
1138c2ecf20Sopenharmony_ci	.mpeg_timing   = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK,
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic struct lgdt3305_config hcw_lgdt3305_config = {
1178c2ecf20Sopenharmony_ci	.i2c_addr           = 0x0e,
1188c2ecf20Sopenharmony_ci	.mpeg_mode          = LGDT3305_MPEG_SERIAL,
1198c2ecf20Sopenharmony_ci	.tpclk_edge         = LGDT3305_TPCLK_FALLING_EDGE,
1208c2ecf20Sopenharmony_ci	.tpvalid_polarity   = LGDT3305_TP_VALID_HIGH,
1218c2ecf20Sopenharmony_ci	.deny_i2c_rptr      = 1,
1228c2ecf20Sopenharmony_ci	.spectral_inversion = 1,
1238c2ecf20Sopenharmony_ci	.qam_if_khz         = 4000,
1248c2ecf20Sopenharmony_ci	.vsb_if_khz         = 3250,
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic struct tda18271_std_map hauppauge_tda18271_std_map = {
1288c2ecf20Sopenharmony_ci	.atsc_6   = { .if_freq = 3250, .agc_mode = 3, .std = 4,
1298c2ecf20Sopenharmony_ci		      .if_lvl = 1, .rfagc_top = 0x58, },
1308c2ecf20Sopenharmony_ci	.qam_6    = { .if_freq = 4000, .agc_mode = 3, .std = 5,
1318c2ecf20Sopenharmony_ci		      .if_lvl = 1, .rfagc_top = 0x58, },
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic struct tda18271_config hcw_tda18271_config = {
1358c2ecf20Sopenharmony_ci	.std_map = &hauppauge_tda18271_std_map,
1368c2ecf20Sopenharmony_ci	.gate    = TDA18271_GATE_DIGITAL,
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic const struct mb86a20s_config pv_mb86a20s_config = {
1408c2ecf20Sopenharmony_ci	.demod_address = 0x10,
1418c2ecf20Sopenharmony_ci	.is_serial = true,
1428c2ecf20Sopenharmony_ci};
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic struct tda18271_config pv_tda18271_config = {
1458c2ecf20Sopenharmony_ci	.std_map = &mb86a20s_tda18271_config,
1468c2ecf20Sopenharmony_ci	.gate    = TDA18271_GATE_DIGITAL,
1478c2ecf20Sopenharmony_ci	.small_i2c = TDA18271_03_BYTE_CHUNK_INIT,
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic const struct lgdt3306a_config hauppauge_955q_lgdt3306a_config = {
1518c2ecf20Sopenharmony_ci	.qam_if_khz         = 4000,
1528c2ecf20Sopenharmony_ci	.vsb_if_khz         = 3250,
1538c2ecf20Sopenharmony_ci	.spectral_inversion = 1,
1548c2ecf20Sopenharmony_ci	.mpeg_mode          = LGDT3306A_MPEG_SERIAL,
1558c2ecf20Sopenharmony_ci	.tpclk_edge         = LGDT3306A_TPCLK_RISING_EDGE,
1568c2ecf20Sopenharmony_ci	.tpvalid_polarity   = LGDT3306A_TP_VALID_HIGH,
1578c2ecf20Sopenharmony_ci	.xtalMHz            = 25,
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic struct r820t_config astrometa_t2hybrid_r820t_config = {
1618c2ecf20Sopenharmony_ci	.i2c_addr		= 0x3a, /* 0x74 >> 1 */
1628c2ecf20Sopenharmony_ci	.xtal			= 16000000,
1638c2ecf20Sopenharmony_ci	.rafael_chip		= CHIP_R828D,
1648c2ecf20Sopenharmony_ci	.max_i2c_msg_len	= 2,
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic inline void print_err_status(struct cx231xx *dev, int packet, int status)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	char *errmsg = "Unknown";
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	switch (status) {
1728c2ecf20Sopenharmony_ci	case -ENOENT:
1738c2ecf20Sopenharmony_ci		errmsg = "unlinked synchronously";
1748c2ecf20Sopenharmony_ci		break;
1758c2ecf20Sopenharmony_ci	case -ECONNRESET:
1768c2ecf20Sopenharmony_ci		errmsg = "unlinked asynchronously";
1778c2ecf20Sopenharmony_ci		break;
1788c2ecf20Sopenharmony_ci	case -ENOSR:
1798c2ecf20Sopenharmony_ci		errmsg = "Buffer error (overrun)";
1808c2ecf20Sopenharmony_ci		break;
1818c2ecf20Sopenharmony_ci	case -EPIPE:
1828c2ecf20Sopenharmony_ci		errmsg = "Stalled (device not responding)";
1838c2ecf20Sopenharmony_ci		break;
1848c2ecf20Sopenharmony_ci	case -EOVERFLOW:
1858c2ecf20Sopenharmony_ci		errmsg = "Babble (bad cable?)";
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci	case -EPROTO:
1888c2ecf20Sopenharmony_ci		errmsg = "Bit-stuff error (bad cable?)";
1898c2ecf20Sopenharmony_ci		break;
1908c2ecf20Sopenharmony_ci	case -EILSEQ:
1918c2ecf20Sopenharmony_ci		errmsg = "CRC/Timeout (could be anything)";
1928c2ecf20Sopenharmony_ci		break;
1938c2ecf20Sopenharmony_ci	case -ETIME:
1948c2ecf20Sopenharmony_ci		errmsg = "Device does not respond";
1958c2ecf20Sopenharmony_ci		break;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci	if (packet < 0) {
1988c2ecf20Sopenharmony_ci		dev_dbg(dev->dev,
1998c2ecf20Sopenharmony_ci			"URB status %d [%s].\n", status, errmsg);
2008c2ecf20Sopenharmony_ci	} else {
2018c2ecf20Sopenharmony_ci		dev_dbg(dev->dev,
2028c2ecf20Sopenharmony_ci			"URB packet %d, status %d [%s].\n",
2038c2ecf20Sopenharmony_ci			packet, status, errmsg);
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	int i;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (!dev)
2128c2ecf20Sopenharmony_ci		return 0;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (dev->state & DEV_DISCONNECTED)
2158c2ecf20Sopenharmony_ci		return 0;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (urb->status < 0) {
2188c2ecf20Sopenharmony_ci		print_err_status(dev, -1, urb->status);
2198c2ecf20Sopenharmony_ci		if (urb->status == -ENOENT)
2208c2ecf20Sopenharmony_ci			return 0;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
2248c2ecf20Sopenharmony_ci		int status = urb->iso_frame_desc[i].status;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		if (status < 0) {
2278c2ecf20Sopenharmony_ci			print_err_status(dev, i, status);
2288c2ecf20Sopenharmony_ci			if (urb->iso_frame_desc[i].status != -EPROTO)
2298c2ecf20Sopenharmony_ci				continue;
2308c2ecf20Sopenharmony_ci		}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		dvb_dmx_swfilter(&dev->dvb->demux,
2338c2ecf20Sopenharmony_ci				 urb->transfer_buffer +
2348c2ecf20Sopenharmony_ci				urb->iso_frame_desc[i].offset,
2358c2ecf20Sopenharmony_ci				urb->iso_frame_desc[i].actual_length);
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return 0;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic inline int dvb_bulk_copy(struct cx231xx *dev, struct urb *urb)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	if (!dev)
2448c2ecf20Sopenharmony_ci		return 0;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (dev->state & DEV_DISCONNECTED)
2478c2ecf20Sopenharmony_ci		return 0;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (urb->status < 0) {
2508c2ecf20Sopenharmony_ci		print_err_status(dev, -1, urb->status);
2518c2ecf20Sopenharmony_ci		if (urb->status == -ENOENT)
2528c2ecf20Sopenharmony_ci			return 0;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* Feed the transport payload into the kernel demux */
2568c2ecf20Sopenharmony_ci	dvb_dmx_swfilter(&dev->dvb->demux,
2578c2ecf20Sopenharmony_ci		urb->transfer_buffer, urb->actual_length);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return 0;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic int start_streaming(struct cx231xx_dvb *dvb)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	int rc;
2658c2ecf20Sopenharmony_ci	struct cx231xx *dev = dvb->adapter.priv;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (dev->USE_ISO) {
2688c2ecf20Sopenharmony_ci		dev_dbg(dev->dev, "DVB transfer mode is ISO.\n");
2698c2ecf20Sopenharmony_ci		cx231xx_set_alt_setting(dev, INDEX_TS1, 5);
2708c2ecf20Sopenharmony_ci		rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
2718c2ecf20Sopenharmony_ci		if (rc < 0)
2728c2ecf20Sopenharmony_ci			return rc;
2738c2ecf20Sopenharmony_ci		dev->mode_tv = 1;
2748c2ecf20Sopenharmony_ci		return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
2758c2ecf20Sopenharmony_ci					CX231XX_DVB_NUM_BUFS,
2768c2ecf20Sopenharmony_ci					dev->ts1_mode.max_pkt_size,
2778c2ecf20Sopenharmony_ci					dvb_isoc_copy);
2788c2ecf20Sopenharmony_ci	} else {
2798c2ecf20Sopenharmony_ci		dev_dbg(dev->dev, "DVB transfer mode is BULK.\n");
2808c2ecf20Sopenharmony_ci		cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
2818c2ecf20Sopenharmony_ci		rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
2828c2ecf20Sopenharmony_ci		if (rc < 0)
2838c2ecf20Sopenharmony_ci			return rc;
2848c2ecf20Sopenharmony_ci		dev->mode_tv = 1;
2858c2ecf20Sopenharmony_ci		return cx231xx_init_bulk(dev, CX231XX_DVB_MAX_PACKETS,
2868c2ecf20Sopenharmony_ci					CX231XX_DVB_NUM_BUFS,
2878c2ecf20Sopenharmony_ci					dev->ts1_mode.max_pkt_size,
2888c2ecf20Sopenharmony_ci					dvb_bulk_copy);
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic int stop_streaming(struct cx231xx_dvb *dvb)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct cx231xx *dev = dvb->adapter.priv;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (dev->USE_ISO)
2988c2ecf20Sopenharmony_ci		cx231xx_uninit_isoc(dev);
2998c2ecf20Sopenharmony_ci	else
3008c2ecf20Sopenharmony_ci		cx231xx_uninit_bulk(dev);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	cx231xx_set_mode(dev, CX231XX_SUSPEND);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return 0;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int start_feed(struct dvb_demux_feed *feed)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct dvb_demux *demux = feed->demux;
3108c2ecf20Sopenharmony_ci	struct cx231xx_dvb *dvb = demux->priv;
3118c2ecf20Sopenharmony_ci	int rc, ret;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (!demux->dmx.frontend)
3148c2ecf20Sopenharmony_ci		return -EINVAL;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	mutex_lock(&dvb->lock);
3178c2ecf20Sopenharmony_ci	dvb->nfeeds++;
3188c2ecf20Sopenharmony_ci	rc = dvb->nfeeds;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (dvb->nfeeds == 1) {
3218c2ecf20Sopenharmony_ci		ret = start_streaming(dvb);
3228c2ecf20Sopenharmony_ci		if (ret < 0)
3238c2ecf20Sopenharmony_ci			rc = ret;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	mutex_unlock(&dvb->lock);
3278c2ecf20Sopenharmony_ci	return rc;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int stop_feed(struct dvb_demux_feed *feed)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct dvb_demux *demux = feed->demux;
3338c2ecf20Sopenharmony_ci	struct cx231xx_dvb *dvb = demux->priv;
3348c2ecf20Sopenharmony_ci	int err = 0;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	mutex_lock(&dvb->lock);
3378c2ecf20Sopenharmony_ci	dvb->nfeeds--;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (0 == dvb->nfeeds)
3408c2ecf20Sopenharmony_ci		err = stop_streaming(dvb);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	mutex_unlock(&dvb->lock);
3438c2ecf20Sopenharmony_ci	return err;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
3478c2ecf20Sopenharmony_cistatic int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct cx231xx *dev = fe->dvb->priv;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (acquire)
3528c2ecf20Sopenharmony_ci		return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
3538c2ecf20Sopenharmony_ci	else
3548c2ecf20Sopenharmony_ci		return cx231xx_set_mode(dev, CX231XX_SUSPEND);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic struct xc5000_config cnxt_rde250_tunerconfig = {
3608c2ecf20Sopenharmony_ci	.i2c_address = 0x61,
3618c2ecf20Sopenharmony_ci	.if_khz = 4000,
3628c2ecf20Sopenharmony_ci};
3638c2ecf20Sopenharmony_cistatic struct xc5000_config cnxt_rdu250_tunerconfig = {
3648c2ecf20Sopenharmony_ci	.i2c_address = 0x61,
3658c2ecf20Sopenharmony_ci	.if_khz = 3250,
3668c2ecf20Sopenharmony_ci};
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
3698c2ecf20Sopenharmony_ci#if 0
3708c2ecf20Sopenharmony_cistatic int attach_xc5000(u8 addr, struct cx231xx *dev)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	struct dvb_frontend *fe;
3748c2ecf20Sopenharmony_ci	struct xc5000_config cfg;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	memset(&cfg, 0, sizeof(cfg));
3778c2ecf20Sopenharmony_ci	cfg.i2c_adap = cx231xx_get_i2c_adap(dev, dev->board.tuner_i2c_master);
3788c2ecf20Sopenharmony_ci	cfg.i2c_addr = addr;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (!dev->dvb->frontend[0]) {
3818c2ecf20Sopenharmony_ci		dev_err(dev->dev, "%s/2: dvb frontend not attached. Can't attach xc5000\n",
3828c2ecf20Sopenharmony_ci			dev->name);
3838c2ecf20Sopenharmony_ci		return -EINVAL;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	fe = dvb_attach(xc5000_attach, dev->dvb->frontend[0], &cfg);
3878c2ecf20Sopenharmony_ci	if (!fe) {
3888c2ecf20Sopenharmony_ci		dev_err(dev->dev, "%s/2: xc5000 attach failed\n", dev->name);
3898c2ecf20Sopenharmony_ci		dvb_frontend_detach(dev->dvb->frontend[0]);
3908c2ecf20Sopenharmony_ci		dev->dvb->frontend[0] = NULL;
3918c2ecf20Sopenharmony_ci		return -EINVAL;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	dev_info(dev->dev, "%s/2: xc5000 attached\n", dev->name);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci#endif
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ciint cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	if (dev->dvb && dev->dvb->frontend[0]) {
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci		struct dvb_tuner_ops *dops = &dev->dvb->frontend[0]->ops.tuner_ops;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		if (dops->set_analog_params != NULL) {
4078c2ecf20Sopenharmony_ci			struct analog_parameters params;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci			params.frequency = freq;
4108c2ecf20Sopenharmony_ci			params.std = dev->norm;
4118c2ecf20Sopenharmony_ci			params.mode = 0;	/* 0- Air; 1 - cable */
4128c2ecf20Sopenharmony_ci			/*params.audmode = ;       */
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci			/* Set the analog parameters to set the frequency */
4158c2ecf20Sopenharmony_ci			dops->set_analog_params(dev->dvb->frontend[0], &params);
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ciint cx231xx_reset_analog_tuner(struct cx231xx *dev)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	int status = 0;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (dev->dvb && dev->dvb->frontend[0]) {
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		struct dvb_tuner_ops *dops = &dev->dvb->frontend[0]->ops.tuner_ops;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		if (dops->init != NULL && !dev->xc_fw_load_done) {
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci			dev_dbg(dev->dev,
4348c2ecf20Sopenharmony_ci				"Reloading firmware for XC5000\n");
4358c2ecf20Sopenharmony_ci			status = dops->init(dev->dvb->frontend[0]);
4368c2ecf20Sopenharmony_ci			if (status == 0) {
4378c2ecf20Sopenharmony_ci				dev->xc_fw_load_done = 1;
4388c2ecf20Sopenharmony_ci				dev_dbg(dev->dev,
4398c2ecf20Sopenharmony_ci					"XC5000 firmware download completed\n");
4408c2ecf20Sopenharmony_ci			} else {
4418c2ecf20Sopenharmony_ci				dev->xc_fw_load_done = 0;
4428c2ecf20Sopenharmony_ci				dev_dbg(dev->dev,
4438c2ecf20Sopenharmony_ci					"XC5000 firmware download failed !!!\n");
4448c2ecf20Sopenharmony_ci			}
4458c2ecf20Sopenharmony_ci		}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return status;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ */
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic int register_dvb(struct cx231xx_dvb *dvb,
4558c2ecf20Sopenharmony_ci			struct module *module,
4568c2ecf20Sopenharmony_ci			struct cx231xx *dev, struct device *device)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	int result;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	mutex_init(&dvb->lock);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* register adapter */
4648c2ecf20Sopenharmony_ci	result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
4658c2ecf20Sopenharmony_ci				      adapter_nr);
4668c2ecf20Sopenharmony_ci	if (result < 0) {
4678c2ecf20Sopenharmony_ci		dev_warn(dev->dev,
4688c2ecf20Sopenharmony_ci		       "%s: dvb_register_adapter failed (errno = %d)\n",
4698c2ecf20Sopenharmony_ci		       dev->name, result);
4708c2ecf20Sopenharmony_ci		goto fail_adapter;
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci	dvb_register_media_controller(&dvb->adapter, dev->media_dev);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* Ensure all frontends negotiate bus access */
4758c2ecf20Sopenharmony_ci	dvb->frontend[0]->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
4768c2ecf20Sopenharmony_ci	if (dvb->frontend[1])
4778c2ecf20Sopenharmony_ci		dvb->frontend[1]->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	dvb->adapter.priv = dev;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* register frontend */
4828c2ecf20Sopenharmony_ci	result = dvb_register_frontend(&dvb->adapter, dvb->frontend[0]);
4838c2ecf20Sopenharmony_ci	if (result < 0) {
4848c2ecf20Sopenharmony_ci		dev_warn(dev->dev,
4858c2ecf20Sopenharmony_ci		       "%s: dvb_register_frontend failed (errno = %d)\n",
4868c2ecf20Sopenharmony_ci		       dev->name, result);
4878c2ecf20Sopenharmony_ci		goto fail_frontend0;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (dvb->frontend[1]) {
4918c2ecf20Sopenharmony_ci		result = dvb_register_frontend(&dvb->adapter, dvb->frontend[1]);
4928c2ecf20Sopenharmony_ci		if (result < 0) {
4938c2ecf20Sopenharmony_ci			dev_warn(dev->dev,
4948c2ecf20Sopenharmony_ci				 "%s: 2nd dvb_register_frontend failed (errno = %d)\n",
4958c2ecf20Sopenharmony_ci				dev->name, result);
4968c2ecf20Sopenharmony_ci			goto fail_frontend1;
4978c2ecf20Sopenharmony_ci		}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		/* MFE lock */
5008c2ecf20Sopenharmony_ci		dvb->adapter.mfe_shared = 1;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	/* register demux stuff */
5048c2ecf20Sopenharmony_ci	dvb->demux.dmx.capabilities =
5058c2ecf20Sopenharmony_ci	    DMX_TS_FILTERING | DMX_SECTION_FILTERING |
5068c2ecf20Sopenharmony_ci	    DMX_MEMORY_BASED_FILTERING;
5078c2ecf20Sopenharmony_ci	dvb->demux.priv = dvb;
5088c2ecf20Sopenharmony_ci	dvb->demux.filternum = 256;
5098c2ecf20Sopenharmony_ci	dvb->demux.feednum = 256;
5108c2ecf20Sopenharmony_ci	dvb->demux.start_feed = start_feed;
5118c2ecf20Sopenharmony_ci	dvb->demux.stop_feed = stop_feed;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	result = dvb_dmx_init(&dvb->demux);
5148c2ecf20Sopenharmony_ci	if (result < 0) {
5158c2ecf20Sopenharmony_ci		dev_warn(dev->dev,
5168c2ecf20Sopenharmony_ci			 "%s: dvb_dmx_init failed (errno = %d)\n",
5178c2ecf20Sopenharmony_ci		       dev->name, result);
5188c2ecf20Sopenharmony_ci		goto fail_dmx;
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	dvb->dmxdev.filternum = 256;
5228c2ecf20Sopenharmony_ci	dvb->dmxdev.demux = &dvb->demux.dmx;
5238c2ecf20Sopenharmony_ci	dvb->dmxdev.capabilities = 0;
5248c2ecf20Sopenharmony_ci	result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
5258c2ecf20Sopenharmony_ci	if (result < 0) {
5268c2ecf20Sopenharmony_ci		dev_warn(dev->dev,
5278c2ecf20Sopenharmony_ci			 "%s: dvb_dmxdev_init failed (errno = %d)\n",
5288c2ecf20Sopenharmony_ci			 dev->name, result);
5298c2ecf20Sopenharmony_ci		goto fail_dmxdev;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	dvb->fe_hw.source = DMX_FRONTEND_0;
5338c2ecf20Sopenharmony_ci	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
5348c2ecf20Sopenharmony_ci	if (result < 0) {
5358c2ecf20Sopenharmony_ci		dev_warn(dev->dev,
5368c2ecf20Sopenharmony_ci		       "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
5378c2ecf20Sopenharmony_ci		       dev->name, result);
5388c2ecf20Sopenharmony_ci		goto fail_fe_hw;
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	dvb->fe_mem.source = DMX_MEMORY_FE;
5428c2ecf20Sopenharmony_ci	result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
5438c2ecf20Sopenharmony_ci	if (result < 0) {
5448c2ecf20Sopenharmony_ci		dev_warn(dev->dev,
5458c2ecf20Sopenharmony_ci			 "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
5468c2ecf20Sopenharmony_ci			 dev->name, result);
5478c2ecf20Sopenharmony_ci		goto fail_fe_mem;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
5518c2ecf20Sopenharmony_ci	if (result < 0) {
5528c2ecf20Sopenharmony_ci		dev_warn(dev->dev,
5538c2ecf20Sopenharmony_ci			 "%s: connect_frontend failed (errno = %d)\n",
5548c2ecf20Sopenharmony_ci			 dev->name, result);
5558c2ecf20Sopenharmony_ci		goto fail_fe_conn;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/* register network adapter */
5598c2ecf20Sopenharmony_ci	dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
5608c2ecf20Sopenharmony_ci	result = dvb_create_media_graph(&dvb->adapter,
5618c2ecf20Sopenharmony_ci					dev->tuner_type == TUNER_ABSENT);
5628c2ecf20Sopenharmony_ci	if (result < 0)
5638c2ecf20Sopenharmony_ci		goto fail_create_graph;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	return 0;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cifail_create_graph:
5688c2ecf20Sopenharmony_ci	dvb_net_release(&dvb->net);
5698c2ecf20Sopenharmony_cifail_fe_conn:
5708c2ecf20Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
5718c2ecf20Sopenharmony_cifail_fe_mem:
5728c2ecf20Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
5738c2ecf20Sopenharmony_cifail_fe_hw:
5748c2ecf20Sopenharmony_ci	dvb_dmxdev_release(&dvb->dmxdev);
5758c2ecf20Sopenharmony_cifail_dmxdev:
5768c2ecf20Sopenharmony_ci	dvb_dmx_release(&dvb->demux);
5778c2ecf20Sopenharmony_cifail_dmx:
5788c2ecf20Sopenharmony_ci	if (dvb->frontend[1])
5798c2ecf20Sopenharmony_ci		dvb_unregister_frontend(dvb->frontend[1]);
5808c2ecf20Sopenharmony_ci	dvb_unregister_frontend(dvb->frontend[0]);
5818c2ecf20Sopenharmony_cifail_frontend1:
5828c2ecf20Sopenharmony_ci	if (dvb->frontend[1])
5838c2ecf20Sopenharmony_ci		dvb_frontend_detach(dvb->frontend[1]);
5848c2ecf20Sopenharmony_cifail_frontend0:
5858c2ecf20Sopenharmony_ci	dvb_frontend_detach(dvb->frontend[0]);
5868c2ecf20Sopenharmony_ci	dvb_unregister_adapter(&dvb->adapter);
5878c2ecf20Sopenharmony_cifail_adapter:
5888c2ecf20Sopenharmony_ci	return result;
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic void unregister_dvb(struct cx231xx_dvb *dvb)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	dvb_net_release(&dvb->net);
5948c2ecf20Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
5958c2ecf20Sopenharmony_ci	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
5968c2ecf20Sopenharmony_ci	dvb_dmxdev_release(&dvb->dmxdev);
5978c2ecf20Sopenharmony_ci	dvb_dmx_release(&dvb->demux);
5988c2ecf20Sopenharmony_ci	if (dvb->frontend[1])
5998c2ecf20Sopenharmony_ci		dvb_unregister_frontend(dvb->frontend[1]);
6008c2ecf20Sopenharmony_ci	dvb_unregister_frontend(dvb->frontend[0]);
6018c2ecf20Sopenharmony_ci	if (dvb->frontend[1])
6028c2ecf20Sopenharmony_ci		dvb_frontend_detach(dvb->frontend[1]);
6038c2ecf20Sopenharmony_ci	dvb_frontend_detach(dvb->frontend[0]);
6048c2ecf20Sopenharmony_ci	dvb_unregister_adapter(&dvb->adapter);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	/* remove I2C tuner */
6078c2ecf20Sopenharmony_ci	dvb_module_release(dvb->i2c_client_tuner);
6088c2ecf20Sopenharmony_ci	dvb->i2c_client_tuner = NULL;
6098c2ecf20Sopenharmony_ci	/* remove I2C demod(s) */
6108c2ecf20Sopenharmony_ci	dvb_module_release(dvb->i2c_client_demod[1]);
6118c2ecf20Sopenharmony_ci	dvb->i2c_client_demod[1] = NULL;
6128c2ecf20Sopenharmony_ci	dvb_module_release(dvb->i2c_client_demod[0]);
6138c2ecf20Sopenharmony_ci	dvb->i2c_client_demod[0] = NULL;
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int dvb_init(struct cx231xx *dev)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	int result;
6198c2ecf20Sopenharmony_ci	struct cx231xx_dvb *dvb;
6208c2ecf20Sopenharmony_ci	struct i2c_adapter *tuner_i2c;
6218c2ecf20Sopenharmony_ci	struct i2c_adapter *demod_i2c;
6228c2ecf20Sopenharmony_ci	struct i2c_client *client;
6238c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	if (!dev->board.has_dvb) {
6268c2ecf20Sopenharmony_ci		/* This device does not support the extension */
6278c2ecf20Sopenharmony_ci		return 0;
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (dvb == NULL) {
6338c2ecf20Sopenharmony_ci		dev_info(dev->dev,
6348c2ecf20Sopenharmony_ci			 "cx231xx_dvb: memory allocation failed\n");
6358c2ecf20Sopenharmony_ci		return -ENOMEM;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci	dev->dvb = dvb;
6388c2ecf20Sopenharmony_ci	dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq;
6398c2ecf20Sopenharmony_ci	dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	tuner_i2c = cx231xx_get_i2c_adap(dev, dev->board.tuner_i2c_master);
6428c2ecf20Sopenharmony_ci	demod_i2c = cx231xx_get_i2c_adap(dev, dev->board.demod_i2c_master);
6438c2ecf20Sopenharmony_ci	mutex_lock(&dev->lock);
6448c2ecf20Sopenharmony_ci	cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
6458c2ecf20Sopenharmony_ci	cx231xx_demod_reset(dev);
6468c2ecf20Sopenharmony_ci	/* init frontend */
6478c2ecf20Sopenharmony_ci	switch (dev->model) {
6488c2ecf20Sopenharmony_ci	case CX231XX_BOARD_CNXT_CARRAERA:
6498c2ecf20Sopenharmony_ci	case CX231XX_BOARD_CNXT_RDE_250:
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci		dev->dvb->frontend[0] = dvb_attach(s5h1432_attach,
6528c2ecf20Sopenharmony_ci					&dvico_s5h1432_config,
6538c2ecf20Sopenharmony_ci					demod_i2c);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci		if (!dev->dvb->frontend[0]) {
6568c2ecf20Sopenharmony_ci			dev_err(dev->dev,
6578c2ecf20Sopenharmony_ci				"Failed to attach s5h1432 front end\n");
6588c2ecf20Sopenharmony_ci			result = -EINVAL;
6598c2ecf20Sopenharmony_ci			goto out_free;
6608c2ecf20Sopenharmony_ci		}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
6638c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci		if (!dvb_attach(xc5000_attach, dev->dvb->frontend[0],
6668c2ecf20Sopenharmony_ci			       tuner_i2c,
6678c2ecf20Sopenharmony_ci			       &cnxt_rde250_tunerconfig)) {
6688c2ecf20Sopenharmony_ci			result = -EINVAL;
6698c2ecf20Sopenharmony_ci			goto out_free;
6708c2ecf20Sopenharmony_ci		}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		break;
6738c2ecf20Sopenharmony_ci	case CX231XX_BOARD_CNXT_SHELBY:
6748c2ecf20Sopenharmony_ci	case CX231XX_BOARD_CNXT_RDU_250:
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci		dev->dvb->frontend[0] = dvb_attach(s5h1411_attach,
6778c2ecf20Sopenharmony_ci					       &xc5000_s5h1411_config,
6788c2ecf20Sopenharmony_ci					       demod_i2c);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci		if (!dev->dvb->frontend[0]) {
6818c2ecf20Sopenharmony_ci			dev_err(dev->dev,
6828c2ecf20Sopenharmony_ci				"Failed to attach s5h1411 front end\n");
6838c2ecf20Sopenharmony_ci			result = -EINVAL;
6848c2ecf20Sopenharmony_ci			goto out_free;
6858c2ecf20Sopenharmony_ci		}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
6888c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		if (!dvb_attach(xc5000_attach, dev->dvb->frontend[0],
6918c2ecf20Sopenharmony_ci			       tuner_i2c,
6928c2ecf20Sopenharmony_ci			       &cnxt_rdu250_tunerconfig)) {
6938c2ecf20Sopenharmony_ci			result = -EINVAL;
6948c2ecf20Sopenharmony_ci			goto out_free;
6958c2ecf20Sopenharmony_ci		}
6968c2ecf20Sopenharmony_ci		break;
6978c2ecf20Sopenharmony_ci	case CX231XX_BOARD_CNXT_RDE_253S:
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci		dev->dvb->frontend[0] = dvb_attach(s5h1432_attach,
7008c2ecf20Sopenharmony_ci					&dvico_s5h1432_config,
7018c2ecf20Sopenharmony_ci					demod_i2c);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci		if (!dev->dvb->frontend[0]) {
7048c2ecf20Sopenharmony_ci			dev_err(dev->dev,
7058c2ecf20Sopenharmony_ci				"Failed to attach s5h1432 front end\n");
7068c2ecf20Sopenharmony_ci			result = -EINVAL;
7078c2ecf20Sopenharmony_ci			goto out_free;
7088c2ecf20Sopenharmony_ci		}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
7118c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci		if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0],
7148c2ecf20Sopenharmony_ci			       dev->board.tuner_addr, tuner_i2c,
7158c2ecf20Sopenharmony_ci			       &cnxt_rde253s_tunerconfig)) {
7168c2ecf20Sopenharmony_ci			result = -EINVAL;
7178c2ecf20Sopenharmony_ci			goto out_free;
7188c2ecf20Sopenharmony_ci		}
7198c2ecf20Sopenharmony_ci		break;
7208c2ecf20Sopenharmony_ci	case CX231XX_BOARD_CNXT_RDU_253S:
7218c2ecf20Sopenharmony_ci	case CX231XX_BOARD_KWORLD_UB445_USB_HYBRID:
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci		dev->dvb->frontend[0] = dvb_attach(s5h1411_attach,
7248c2ecf20Sopenharmony_ci					       &tda18271_s5h1411_config,
7258c2ecf20Sopenharmony_ci					       demod_i2c);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci		if (!dev->dvb->frontend[0]) {
7288c2ecf20Sopenharmony_ci			dev_err(dev->dev,
7298c2ecf20Sopenharmony_ci				"Failed to attach s5h1411 front end\n");
7308c2ecf20Sopenharmony_ci			result = -EINVAL;
7318c2ecf20Sopenharmony_ci			goto out_free;
7328c2ecf20Sopenharmony_ci		}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
7358c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci		if (!dvb_attach(tda18271_attach, dev->dvb->frontend[0],
7388c2ecf20Sopenharmony_ci			       dev->board.tuner_addr, tuner_i2c,
7398c2ecf20Sopenharmony_ci			       &cnxt_rde253s_tunerconfig)) {
7408c2ecf20Sopenharmony_ci			result = -EINVAL;
7418c2ecf20Sopenharmony_ci			goto out_free;
7428c2ecf20Sopenharmony_ci		}
7438c2ecf20Sopenharmony_ci		break;
7448c2ecf20Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_EXETER:
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci		dev_info(dev->dev,
7478c2ecf20Sopenharmony_ci			 "%s: looking for tuner / demod on i2c bus: %d\n",
7488c2ecf20Sopenharmony_ci		       __func__, i2c_adapter_id(tuner_i2c));
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci		dev->dvb->frontend[0] = dvb_attach(lgdt3305_attach,
7518c2ecf20Sopenharmony_ci						&hcw_lgdt3305_config,
7528c2ecf20Sopenharmony_ci						demod_i2c);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci		if (!dev->dvb->frontend[0]) {
7558c2ecf20Sopenharmony_ci			dev_err(dev->dev,
7568c2ecf20Sopenharmony_ci				"Failed to attach LG3305 front end\n");
7578c2ecf20Sopenharmony_ci			result = -EINVAL;
7588c2ecf20Sopenharmony_ci			goto out_free;
7598c2ecf20Sopenharmony_ci		}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
7628c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci		dvb_attach(tda18271_attach, dev->dvb->frontend[0],
7658c2ecf20Sopenharmony_ci			   dev->board.tuner_addr, tuner_i2c,
7668c2ecf20Sopenharmony_ci			   &hcw_tda18271_config);
7678c2ecf20Sopenharmony_ci		break;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1113xx:
7708c2ecf20Sopenharmony_ci	{
7718c2ecf20Sopenharmony_ci		struct si2165_platform_data si2165_pdata = {};
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci		/* attach demod */
7748c2ecf20Sopenharmony_ci		si2165_pdata.fe = &dev->dvb->frontend[0];
7758c2ecf20Sopenharmony_ci		si2165_pdata.chip_mode = SI2165_MODE_PLL_XTAL;
7768c2ecf20Sopenharmony_ci		si2165_pdata.ref_freq_hz = 16000000;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
7798c2ecf20Sopenharmony_ci		client = dvb_module_probe("si2165", NULL, demod_i2c,
7808c2ecf20Sopenharmony_ci						dev->board.demod_addr,
7818c2ecf20Sopenharmony_ci						&si2165_pdata);
7828c2ecf20Sopenharmony_ci		if (!client) {
7838c2ecf20Sopenharmony_ci			result = -ENODEV;
7848c2ecf20Sopenharmony_ci			goto out_free;
7858c2ecf20Sopenharmony_ci		}
7868c2ecf20Sopenharmony_ci		dvb->i2c_client_demod[0] = client;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci		dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
7918c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci		dvb_attach(tda18271_attach, dev->dvb->frontend[0],
7948c2ecf20Sopenharmony_ci			dev->board.tuner_addr, tuner_i2c,
7958c2ecf20Sopenharmony_ci			&hcw_tda18271_config);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		dev->cx231xx_reset_analog_tuner = NULL;
7988c2ecf20Sopenharmony_ci		break;
7998c2ecf20Sopenharmony_ci	}
8008c2ecf20Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx:
8018c2ecf20Sopenharmony_ci	{
8028c2ecf20Sopenharmony_ci		struct si2165_platform_data si2165_pdata = {};
8038c2ecf20Sopenharmony_ci		struct si2157_config si2157_config = {};
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci		/* attach demod */
8068c2ecf20Sopenharmony_ci		si2165_pdata.fe = &dev->dvb->frontend[0];
8078c2ecf20Sopenharmony_ci		si2165_pdata.chip_mode = SI2165_MODE_PLL_EXT;
8088c2ecf20Sopenharmony_ci		si2165_pdata.ref_freq_hz = 24000000;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
8118c2ecf20Sopenharmony_ci		client = dvb_module_probe("si2165", NULL, demod_i2c,
8128c2ecf20Sopenharmony_ci						dev->board.demod_addr,
8138c2ecf20Sopenharmony_ci						&si2165_pdata);
8148c2ecf20Sopenharmony_ci		if (!client) {
8158c2ecf20Sopenharmony_ci			result = -ENODEV;
8168c2ecf20Sopenharmony_ci			goto out_free;
8178c2ecf20Sopenharmony_ci		}
8188c2ecf20Sopenharmony_ci		dvb->i2c_client_demod[0] = client;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci		dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
8238c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci		/* attach tuner */
8268c2ecf20Sopenharmony_ci		si2157_config.fe = dev->dvb->frontend[0];
8278c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER_DVB
8288c2ecf20Sopenharmony_ci		si2157_config.mdev = dev->media_dev;
8298c2ecf20Sopenharmony_ci#endif
8308c2ecf20Sopenharmony_ci		si2157_config.if_port = 1;
8318c2ecf20Sopenharmony_ci		si2157_config.inversion = true;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
8348c2ecf20Sopenharmony_ci		client = dvb_module_probe("si2157", NULL, tuner_i2c,
8358c2ecf20Sopenharmony_ci						dev->board.tuner_addr,
8368c2ecf20Sopenharmony_ci						&si2157_config);
8378c2ecf20Sopenharmony_ci		if (!client) {
8388c2ecf20Sopenharmony_ci			result = -ENODEV;
8398c2ecf20Sopenharmony_ci			goto out_free;
8408c2ecf20Sopenharmony_ci		}
8418c2ecf20Sopenharmony_ci		dev->cx231xx_reset_analog_tuner = NULL;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci		dev->dvb->i2c_client_tuner = client;
8448c2ecf20Sopenharmony_ci		break;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_955Q:
8478c2ecf20Sopenharmony_ci	{
8488c2ecf20Sopenharmony_ci		struct si2157_config si2157_config = {};
8498c2ecf20Sopenharmony_ci		struct lgdt3306a_config lgdt3306a_config = {};
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci		lgdt3306a_config = hauppauge_955q_lgdt3306a_config;
8528c2ecf20Sopenharmony_ci		lgdt3306a_config.fe = &dev->dvb->frontend[0];
8538c2ecf20Sopenharmony_ci		lgdt3306a_config.i2c_adapter = &adapter;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
8568c2ecf20Sopenharmony_ci		client = dvb_module_probe("lgdt3306a", NULL, demod_i2c,
8578c2ecf20Sopenharmony_ci						dev->board.demod_addr,
8588c2ecf20Sopenharmony_ci						&lgdt3306a_config);
8598c2ecf20Sopenharmony_ci		if (!client) {
8608c2ecf20Sopenharmony_ci			result = -ENODEV;
8618c2ecf20Sopenharmony_ci			goto out_free;
8628c2ecf20Sopenharmony_ci		}
8638c2ecf20Sopenharmony_ci		dvb->i2c_client_demod[0] = client;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci		dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
8688c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci		/* attach tuner */
8718c2ecf20Sopenharmony_ci		si2157_config.fe = dev->dvb->frontend[0];
8728c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER_DVB
8738c2ecf20Sopenharmony_ci		si2157_config.mdev = dev->media_dev;
8748c2ecf20Sopenharmony_ci#endif
8758c2ecf20Sopenharmony_ci		si2157_config.if_port = 1;
8768c2ecf20Sopenharmony_ci		si2157_config.inversion = true;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
8798c2ecf20Sopenharmony_ci		client = dvb_module_probe("si2157", NULL, tuner_i2c,
8808c2ecf20Sopenharmony_ci						dev->board.tuner_addr,
8818c2ecf20Sopenharmony_ci						&si2157_config);
8828c2ecf20Sopenharmony_ci		if (!client) {
8838c2ecf20Sopenharmony_ci			result = -ENODEV;
8848c2ecf20Sopenharmony_ci			goto out_free;
8858c2ecf20Sopenharmony_ci		}
8868c2ecf20Sopenharmony_ci		dev->cx231xx_reset_analog_tuner = NULL;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci		dev->dvb->i2c_client_tuner = client;
8898c2ecf20Sopenharmony_ci		break;
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci	case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
8928c2ecf20Sopenharmony_ci	case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID:
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci		dev_info(dev->dev,
8958c2ecf20Sopenharmony_ci			 "%s: looking for demod on i2c bus: %d\n",
8968c2ecf20Sopenharmony_ci			 __func__, i2c_adapter_id(tuner_i2c));
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci		dev->dvb->frontend[0] = dvb_attach(mb86a20s_attach,
8998c2ecf20Sopenharmony_ci						&pv_mb86a20s_config,
9008c2ecf20Sopenharmony_ci						demod_i2c);
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci		if (!dev->dvb->frontend[0]) {
9038c2ecf20Sopenharmony_ci			dev_err(dev->dev,
9048c2ecf20Sopenharmony_ci				"Failed to attach mb86a20s demod\n");
9058c2ecf20Sopenharmony_ci			result = -EINVAL;
9068c2ecf20Sopenharmony_ci			goto out_free;
9078c2ecf20Sopenharmony_ci		}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
9108c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci		dvb_attach(tda18271_attach, dev->dvb->frontend[0],
9138c2ecf20Sopenharmony_ci			   dev->board.tuner_addr, tuner_i2c,
9148c2ecf20Sopenharmony_ci			   &pv_tda18271_config);
9158c2ecf20Sopenharmony_ci		break;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	case CX231XX_BOARD_EVROMEDIA_FULL_HYBRID_FULLHD:
9188c2ecf20Sopenharmony_ci	{
9198c2ecf20Sopenharmony_ci		struct si2157_config si2157_config = {};
9208c2ecf20Sopenharmony_ci		struct si2168_config si2168_config = {};
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci		/* attach demodulator chip */
9238c2ecf20Sopenharmony_ci		si2168_config.ts_mode = SI2168_TS_SERIAL; /* from *.inf file */
9248c2ecf20Sopenharmony_ci		si2168_config.fe = &dev->dvb->frontend[0];
9258c2ecf20Sopenharmony_ci		si2168_config.i2c_adapter = &adapter;
9268c2ecf20Sopenharmony_ci		si2168_config.ts_clock_inv = true;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
9298c2ecf20Sopenharmony_ci		client = dvb_module_probe("si2168", NULL, demod_i2c,
9308c2ecf20Sopenharmony_ci						dev->board.demod_addr,
9318c2ecf20Sopenharmony_ci						&si2168_config);
9328c2ecf20Sopenharmony_ci		if (!client) {
9338c2ecf20Sopenharmony_ci			result = -ENODEV;
9348c2ecf20Sopenharmony_ci			goto out_free;
9358c2ecf20Sopenharmony_ci		}
9368c2ecf20Sopenharmony_ci		dvb->i2c_client_demod[0] = client;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci		/* attach tuner chip */
9398c2ecf20Sopenharmony_ci		si2157_config.fe = dev->dvb->frontend[0];
9408c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER_DVB
9418c2ecf20Sopenharmony_ci		si2157_config.mdev = dev->media_dev;
9428c2ecf20Sopenharmony_ci#endif
9438c2ecf20Sopenharmony_ci		si2157_config.if_port = 1;
9448c2ecf20Sopenharmony_ci		si2157_config.inversion = false;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
9478c2ecf20Sopenharmony_ci		client = dvb_module_probe("si2157", NULL, tuner_i2c,
9488c2ecf20Sopenharmony_ci						dev->board.tuner_addr,
9498c2ecf20Sopenharmony_ci						&si2157_config);
9508c2ecf20Sopenharmony_ci		if (!client) {
9518c2ecf20Sopenharmony_ci			result = -ENODEV;
9528c2ecf20Sopenharmony_ci			goto out_free;
9538c2ecf20Sopenharmony_ci		}
9548c2ecf20Sopenharmony_ci		dev->cx231xx_reset_analog_tuner = NULL;
9558c2ecf20Sopenharmony_ci		dev->dvb->i2c_client_tuner = client;
9568c2ecf20Sopenharmony_ci		break;
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci	case CX231XX_BOARD_ASTROMETA_T2HYBRID:
9598c2ecf20Sopenharmony_ci	{
9608c2ecf20Sopenharmony_ci		struct mn88473_config mn88473_config = {};
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci		/* attach demodulator chip */
9638c2ecf20Sopenharmony_ci		mn88473_config.i2c_wr_max = 16;
9648c2ecf20Sopenharmony_ci		mn88473_config.xtal = 25000000;
9658c2ecf20Sopenharmony_ci		mn88473_config.fe = &dev->dvb->frontend[0];
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
9688c2ecf20Sopenharmony_ci		client = dvb_module_probe("mn88473", NULL, demod_i2c,
9698c2ecf20Sopenharmony_ci						dev->board.demod_addr,
9708c2ecf20Sopenharmony_ci						&mn88473_config);
9718c2ecf20Sopenharmony_ci		if (!client) {
9728c2ecf20Sopenharmony_ci			result = -ENODEV;
9738c2ecf20Sopenharmony_ci			goto out_free;
9748c2ecf20Sopenharmony_ci		}
9758c2ecf20Sopenharmony_ci		dvb->i2c_client_demod[0] = client;
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
9788c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		/* attach tuner chip */
9818c2ecf20Sopenharmony_ci		dvb_attach(r820t_attach, dev->dvb->frontend[0],
9828c2ecf20Sopenharmony_ci			   tuner_i2c,
9838c2ecf20Sopenharmony_ci			   &astrometa_t2hybrid_r820t_config);
9848c2ecf20Sopenharmony_ci		break;
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_935C:
9878c2ecf20Sopenharmony_ci	{
9888c2ecf20Sopenharmony_ci		struct si2157_config si2157_config = {};
9898c2ecf20Sopenharmony_ci		struct si2168_config si2168_config = {};
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci		/* attach demodulator chip */
9928c2ecf20Sopenharmony_ci		si2168_config.ts_mode = SI2168_TS_SERIAL;
9938c2ecf20Sopenharmony_ci		si2168_config.fe = &dev->dvb->frontend[0];
9948c2ecf20Sopenharmony_ci		si2168_config.i2c_adapter = &adapter;
9958c2ecf20Sopenharmony_ci		si2168_config.ts_clock_inv = true;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
9988c2ecf20Sopenharmony_ci		client = dvb_module_probe("si2168", NULL, demod_i2c,
9998c2ecf20Sopenharmony_ci						dev->board.demod_addr,
10008c2ecf20Sopenharmony_ci						&si2168_config);
10018c2ecf20Sopenharmony_ci		if (!client) {
10028c2ecf20Sopenharmony_ci			result = -ENODEV;
10038c2ecf20Sopenharmony_ci			goto out_free;
10048c2ecf20Sopenharmony_ci		}
10058c2ecf20Sopenharmony_ci		dvb->i2c_client_demod[0] = client;
10068c2ecf20Sopenharmony_ci		dev->dvb->frontend[0]->ops.i2c_gate_ctrl = NULL;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
10098c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci		/* attach tuner */
10128c2ecf20Sopenharmony_ci		si2157_config.fe = dev->dvb->frontend[0];
10138c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER_DVB
10148c2ecf20Sopenharmony_ci		si2157_config.mdev = dev->media_dev;
10158c2ecf20Sopenharmony_ci#endif
10168c2ecf20Sopenharmony_ci		si2157_config.if_port = 1;
10178c2ecf20Sopenharmony_ci		si2157_config.inversion = true;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
10208c2ecf20Sopenharmony_ci		client = dvb_module_probe("si2157", NULL, tuner_i2c,
10218c2ecf20Sopenharmony_ci						dev->board.tuner_addr,
10228c2ecf20Sopenharmony_ci						&si2157_config);
10238c2ecf20Sopenharmony_ci		if (!client) {
10248c2ecf20Sopenharmony_ci			result = -ENODEV;
10258c2ecf20Sopenharmony_ci			goto out_free;
10268c2ecf20Sopenharmony_ci		}
10278c2ecf20Sopenharmony_ci		dev->cx231xx_reset_analog_tuner = NULL;
10288c2ecf20Sopenharmony_ci		dev->dvb->i2c_client_tuner = client;
10298c2ecf20Sopenharmony_ci		break;
10308c2ecf20Sopenharmony_ci	}
10318c2ecf20Sopenharmony_ci	case CX231XX_BOARD_HAUPPAUGE_975:
10328c2ecf20Sopenharmony_ci	{
10338c2ecf20Sopenharmony_ci		struct i2c_adapter *adapter2;
10348c2ecf20Sopenharmony_ci		struct si2157_config si2157_config = {};
10358c2ecf20Sopenharmony_ci		struct lgdt3306a_config lgdt3306a_config = {};
10368c2ecf20Sopenharmony_ci		struct si2168_config si2168_config = {};
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci		/* attach first demodulator chip */
10398c2ecf20Sopenharmony_ci		lgdt3306a_config = hauppauge_955q_lgdt3306a_config;
10408c2ecf20Sopenharmony_ci		lgdt3306a_config.fe = &dev->dvb->frontend[0];
10418c2ecf20Sopenharmony_ci		lgdt3306a_config.i2c_adapter = &adapter;
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
10448c2ecf20Sopenharmony_ci		client = dvb_module_probe("lgdt3306a", NULL, demod_i2c,
10458c2ecf20Sopenharmony_ci						dev->board.demod_addr,
10468c2ecf20Sopenharmony_ci						&lgdt3306a_config);
10478c2ecf20Sopenharmony_ci		if (!client) {
10488c2ecf20Sopenharmony_ci			result = -ENODEV;
10498c2ecf20Sopenharmony_ci			goto out_free;
10508c2ecf20Sopenharmony_ci		}
10518c2ecf20Sopenharmony_ci		dvb->i2c_client_demod[0] = client;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci		/* attach second demodulator chip */
10548c2ecf20Sopenharmony_ci		si2168_config.ts_mode = SI2168_TS_SERIAL;
10558c2ecf20Sopenharmony_ci		si2168_config.fe = &dev->dvb->frontend[1];
10568c2ecf20Sopenharmony_ci		si2168_config.i2c_adapter = &adapter2;
10578c2ecf20Sopenharmony_ci		si2168_config.ts_clock_inv = true;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
10608c2ecf20Sopenharmony_ci		client = dvb_module_probe("si2168", NULL, adapter,
10618c2ecf20Sopenharmony_ci						dev->board.demod_addr2,
10628c2ecf20Sopenharmony_ci						&si2168_config);
10638c2ecf20Sopenharmony_ci		if (!client) {
10648c2ecf20Sopenharmony_ci			result = -ENODEV;
10658c2ecf20Sopenharmony_ci			goto out_free;
10668c2ecf20Sopenharmony_ci		}
10678c2ecf20Sopenharmony_ci		dvb->i2c_client_demod[1] = client;
10688c2ecf20Sopenharmony_ci		dvb->frontend[1]->id = 1;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci		/* define general-purpose callback pointer */
10718c2ecf20Sopenharmony_ci		dvb->frontend[0]->callback = cx231xx_tuner_callback;
10728c2ecf20Sopenharmony_ci		dvb->frontend[1]->callback = cx231xx_tuner_callback;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci		/* attach tuner */
10758c2ecf20Sopenharmony_ci		si2157_config.fe = dev->dvb->frontend[0];
10768c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER_DVB
10778c2ecf20Sopenharmony_ci		si2157_config.mdev = dev->media_dev;
10788c2ecf20Sopenharmony_ci#endif
10798c2ecf20Sopenharmony_ci		si2157_config.if_port = 1;
10808c2ecf20Sopenharmony_ci		si2157_config.inversion = true;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci		/* perform probe/init/attach */
10838c2ecf20Sopenharmony_ci		client = dvb_module_probe("si2157", NULL, adapter,
10848c2ecf20Sopenharmony_ci						dev->board.tuner_addr,
10858c2ecf20Sopenharmony_ci						&si2157_config);
10868c2ecf20Sopenharmony_ci		if (!client) {
10878c2ecf20Sopenharmony_ci			result = -ENODEV;
10888c2ecf20Sopenharmony_ci			goto out_free;
10898c2ecf20Sopenharmony_ci		}
10908c2ecf20Sopenharmony_ci		dev->cx231xx_reset_analog_tuner = NULL;
10918c2ecf20Sopenharmony_ci		dvb->i2c_client_tuner = client;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci		dvb->frontend[1]->tuner_priv = dvb->frontend[0]->tuner_priv;
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci		memcpy(&dvb->frontend[1]->ops.tuner_ops,
10968c2ecf20Sopenharmony_ci			&dvb->frontend[0]->ops.tuner_ops,
10978c2ecf20Sopenharmony_ci			sizeof(struct dvb_tuner_ops));
10988c2ecf20Sopenharmony_ci		break;
10998c2ecf20Sopenharmony_ci	}
11008c2ecf20Sopenharmony_ci	default:
11018c2ecf20Sopenharmony_ci		dev_err(dev->dev,
11028c2ecf20Sopenharmony_ci			"%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
11038c2ecf20Sopenharmony_ci			dev->name);
11048c2ecf20Sopenharmony_ci		break;
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci	if (!dvb->frontend[0]) {
11078c2ecf20Sopenharmony_ci		dev_err(dev->dev,
11088c2ecf20Sopenharmony_ci		       "%s/2: frontend initialization failed\n", dev->name);
11098c2ecf20Sopenharmony_ci		result = -EINVAL;
11108c2ecf20Sopenharmony_ci		goto out_free;
11118c2ecf20Sopenharmony_ci	}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	/* register everything */
11148c2ecf20Sopenharmony_ci	result = register_dvb(dvb, THIS_MODULE, dev, dev->dev);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (result < 0)
11178c2ecf20Sopenharmony_ci		goto out_free;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	dev_info(dev->dev, "Successfully loaded cx231xx-dvb\n");
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ciret:
11238c2ecf20Sopenharmony_ci	cx231xx_set_mode(dev, CX231XX_SUSPEND);
11248c2ecf20Sopenharmony_ci	mutex_unlock(&dev->lock);
11258c2ecf20Sopenharmony_ci	return result;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ciout_free:
11288c2ecf20Sopenharmony_ci	/* remove I2C tuner */
11298c2ecf20Sopenharmony_ci	dvb_module_release(dvb->i2c_client_tuner);
11308c2ecf20Sopenharmony_ci	dvb->i2c_client_tuner = NULL;
11318c2ecf20Sopenharmony_ci	/* remove I2C demod(s) */
11328c2ecf20Sopenharmony_ci	dvb_module_release(dvb->i2c_client_demod[1]);
11338c2ecf20Sopenharmony_ci	dvb->i2c_client_demod[1] = NULL;
11348c2ecf20Sopenharmony_ci	dvb_module_release(dvb->i2c_client_demod[0]);
11358c2ecf20Sopenharmony_ci	dvb->i2c_client_demod[0] = NULL;
11368c2ecf20Sopenharmony_ci	kfree(dvb);
11378c2ecf20Sopenharmony_ci	dev->dvb = NULL;
11388c2ecf20Sopenharmony_ci	goto ret;
11398c2ecf20Sopenharmony_ci}
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_cistatic int dvb_fini(struct cx231xx *dev)
11428c2ecf20Sopenharmony_ci{
11438c2ecf20Sopenharmony_ci	if (!dev->board.has_dvb) {
11448c2ecf20Sopenharmony_ci		/* This device does not support the extension */
11458c2ecf20Sopenharmony_ci		return 0;
11468c2ecf20Sopenharmony_ci	}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	if (dev->dvb) {
11498c2ecf20Sopenharmony_ci		unregister_dvb(dev->dvb);
11508c2ecf20Sopenharmony_ci		kfree(dev->dvb);
11518c2ecf20Sopenharmony_ci		dev->dvb = NULL;
11528c2ecf20Sopenharmony_ci	}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	return 0;
11558c2ecf20Sopenharmony_ci}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cistatic struct cx231xx_ops dvb_ops = {
11588c2ecf20Sopenharmony_ci	.id = CX231XX_DVB,
11598c2ecf20Sopenharmony_ci	.name = "Cx231xx dvb Extension",
11608c2ecf20Sopenharmony_ci	.init = dvb_init,
11618c2ecf20Sopenharmony_ci	.fini = dvb_fini,
11628c2ecf20Sopenharmony_ci};
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_cistatic int __init cx231xx_dvb_register(void)
11658c2ecf20Sopenharmony_ci{
11668c2ecf20Sopenharmony_ci	return cx231xx_register_extension(&dvb_ops);
11678c2ecf20Sopenharmony_ci}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_cistatic void __exit cx231xx_dvb_unregister(void)
11708c2ecf20Sopenharmony_ci{
11718c2ecf20Sopenharmony_ci	cx231xx_unregister_extension(&dvb_ops);
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_cimodule_init(cx231xx_dvb_register);
11758c2ecf20Sopenharmony_cimodule_exit(cx231xx_dvb_unregister);
1176