162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// xc2028
362306a36Sopenharmony_ci//
462306a36Sopenharmony_ci// Copyright (c) 2007-2008 Mauro Carvalho Chehab <mchehab@kernel.org>
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
762306a36Sopenharmony_ci//       - frontend interface
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/i2c.h>
1062306a36Sopenharmony_ci#include <asm/div64.h>
1162306a36Sopenharmony_ci#include <linux/firmware.h>
1262306a36Sopenharmony_ci#include <linux/videodev2.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <media/tuner.h>
1562306a36Sopenharmony_ci#include <linux/mutex.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <asm/unaligned.h>
1862306a36Sopenharmony_ci#include "tuner-i2c.h"
1962306a36Sopenharmony_ci#include "xc2028.h"
2062306a36Sopenharmony_ci#include "xc2028-types.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/dvb/frontend.h>
2362306a36Sopenharmony_ci#include <media/dvb_frontend.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* Max transfer size done by I2C transfer functions */
2662306a36Sopenharmony_ci#define MAX_XFER_SIZE  80
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* Registers (Write-only) */
2962306a36Sopenharmony_ci#define XREG_INIT         0x00
3062306a36Sopenharmony_ci#define XREG_RF_FREQ      0x02
3162306a36Sopenharmony_ci#define XREG_POWER_DOWN   0x08
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* Registers (Read-only) */
3462306a36Sopenharmony_ci#define XREG_FREQ_ERROR   0x01
3562306a36Sopenharmony_ci#define XREG_LOCK         0x02
3662306a36Sopenharmony_ci#define XREG_VERSION      0x04
3762306a36Sopenharmony_ci#define XREG_PRODUCT_ID   0x08
3862306a36Sopenharmony_ci#define XREG_HSYNC_FREQ   0x10
3962306a36Sopenharmony_ci#define XREG_FRAME_LINES  0x20
4062306a36Sopenharmony_ci#define XREG_SNR          0x40
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define XREG_ADC_ENV      0x0100
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int debug;
4562306a36Sopenharmony_cimodule_param(debug, int, 0644);
4662306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "enable verbose debug messages");
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int no_poweroff;
4962306a36Sopenharmony_cimodule_param(no_poweroff, int, 0644);
5062306a36Sopenharmony_ciMODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n"
5162306a36Sopenharmony_ci	"1 keep device energized and with tuner ready all the times.\n"
5262306a36Sopenharmony_ci	"  Faster, but consumes more power and keeps the device hotter\n");
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic char audio_std[8];
5562306a36Sopenharmony_cimodule_param_string(audio_std, audio_std, sizeof(audio_std), 0);
5662306a36Sopenharmony_ciMODULE_PARM_DESC(audio_std,
5762306a36Sopenharmony_ci	"Audio standard. XC3028 audio decoder explicitly needs to know what audio\n"
5862306a36Sopenharmony_ci	"standard is needed for some video standards with audio A2 or NICAM.\n"
5962306a36Sopenharmony_ci	"The valid values are:\n"
6062306a36Sopenharmony_ci	"A2\n"
6162306a36Sopenharmony_ci	"A2/A\n"
6262306a36Sopenharmony_ci	"A2/B\n"
6362306a36Sopenharmony_ci	"NICAM\n"
6462306a36Sopenharmony_ci	"NICAM/A\n"
6562306a36Sopenharmony_ci	"NICAM/B\n");
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic char firmware_name[30];
6862306a36Sopenharmony_cimodule_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
6962306a36Sopenharmony_ciMODULE_PARM_DESC(firmware_name,
7062306a36Sopenharmony_ci		 "Firmware file name. Allows overriding the default firmware name\n");
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic LIST_HEAD(hybrid_tuner_instance_list);
7362306a36Sopenharmony_cistatic DEFINE_MUTEX(xc2028_list_mutex);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* struct for storing firmware table */
7662306a36Sopenharmony_cistruct firmware_description {
7762306a36Sopenharmony_ci	unsigned int  type;
7862306a36Sopenharmony_ci	v4l2_std_id   id;
7962306a36Sopenharmony_ci	__u16         int_freq;
8062306a36Sopenharmony_ci	unsigned char *ptr;
8162306a36Sopenharmony_ci	unsigned int  size;
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistruct firmware_properties {
8562306a36Sopenharmony_ci	unsigned int	type;
8662306a36Sopenharmony_ci	v4l2_std_id	id;
8762306a36Sopenharmony_ci	v4l2_std_id	std_req;
8862306a36Sopenharmony_ci	__u16		int_freq;
8962306a36Sopenharmony_ci	unsigned int	scode_table;
9062306a36Sopenharmony_ci	int		scode_nr;
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cienum xc2028_state {
9462306a36Sopenharmony_ci	XC2028_NO_FIRMWARE = 0,
9562306a36Sopenharmony_ci	XC2028_WAITING_FIRMWARE,
9662306a36Sopenharmony_ci	XC2028_ACTIVE,
9762306a36Sopenharmony_ci	XC2028_SLEEP,
9862306a36Sopenharmony_ci	XC2028_NODEV,
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistruct xc2028_data {
10262306a36Sopenharmony_ci	struct list_head        hybrid_tuner_instance_list;
10362306a36Sopenharmony_ci	struct tuner_i2c_props  i2c_props;
10462306a36Sopenharmony_ci	__u32			frequency;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	enum xc2028_state	state;
10762306a36Sopenharmony_ci	const char		*fname;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	struct firmware_description *firm;
11062306a36Sopenharmony_ci	int			firm_size;
11162306a36Sopenharmony_ci	__u16			firm_version;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	__u16			hwmodel;
11462306a36Sopenharmony_ci	__u16			hwvers;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	struct xc2028_ctrl	ctrl;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	struct firmware_properties cur_fw;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	struct mutex lock;
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define i2c_send(priv, buf, size) ({					\
12462306a36Sopenharmony_ci	int _rc;							\
12562306a36Sopenharmony_ci	_rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size);		\
12662306a36Sopenharmony_ci	if (size != _rc)						\
12762306a36Sopenharmony_ci		tuner_info("i2c output error: rc = %d (should be %d)\n",\
12862306a36Sopenharmony_ci			   _rc, (int)size);				\
12962306a36Sopenharmony_ci	if (priv->ctrl.msleep)						\
13062306a36Sopenharmony_ci		msleep(priv->ctrl.msleep);				\
13162306a36Sopenharmony_ci	_rc;								\
13262306a36Sopenharmony_ci})
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({		\
13562306a36Sopenharmony_ci	int _rc;							\
13662306a36Sopenharmony_ci	_rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize,	\
13762306a36Sopenharmony_ci				       ibuf, isize);			\
13862306a36Sopenharmony_ci	if (isize != _rc)						\
13962306a36Sopenharmony_ci		tuner_err("i2c input error: rc = %d (should be %d)\n",	\
14062306a36Sopenharmony_ci			   _rc, (int)isize);				\
14162306a36Sopenharmony_ci	if (priv->ctrl.msleep)						\
14262306a36Sopenharmony_ci		msleep(priv->ctrl.msleep);				\
14362306a36Sopenharmony_ci	_rc;								\
14462306a36Sopenharmony_ci})
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci#define send_seq(priv, data...)	({					\
14762306a36Sopenharmony_ci	static u8 _val[] = data;					\
14862306a36Sopenharmony_ci	int _rc;							\
14962306a36Sopenharmony_ci	if (sizeof(_val) !=						\
15062306a36Sopenharmony_ci			(_rc = tuner_i2c_xfer_send(&priv->i2c_props,	\
15162306a36Sopenharmony_ci						_val, sizeof(_val)))) {	\
15262306a36Sopenharmony_ci		tuner_err("Error on line %d: %d\n", __LINE__, _rc);	\
15362306a36Sopenharmony_ci	} else if (priv->ctrl.msleep)					\
15462306a36Sopenharmony_ci		msleep(priv->ctrl.msleep);				\
15562306a36Sopenharmony_ci	_rc;								\
15662306a36Sopenharmony_ci})
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	unsigned char buf[2];
16162306a36Sopenharmony_ci	unsigned char ibuf[2];
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	tuner_dbg("%s %04x called\n", __func__, reg);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	buf[0] = reg >> 8;
16662306a36Sopenharmony_ci	buf[1] = (unsigned char) reg;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)
16962306a36Sopenharmony_ci		return -EIO;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	*val = (ibuf[1]) | (ibuf[0] << 8);
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci#define dump_firm_type(t)	dump_firm_type_and_int_freq(t, 0)
17662306a36Sopenharmony_cistatic void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	if (type & BASE)
17962306a36Sopenharmony_ci		printk(KERN_CONT "BASE ");
18062306a36Sopenharmony_ci	if (type & INIT1)
18162306a36Sopenharmony_ci		printk(KERN_CONT "INIT1 ");
18262306a36Sopenharmony_ci	if (type & F8MHZ)
18362306a36Sopenharmony_ci		printk(KERN_CONT "F8MHZ ");
18462306a36Sopenharmony_ci	if (type & MTS)
18562306a36Sopenharmony_ci		printk(KERN_CONT "MTS ");
18662306a36Sopenharmony_ci	if (type & D2620)
18762306a36Sopenharmony_ci		printk(KERN_CONT "D2620 ");
18862306a36Sopenharmony_ci	if (type & D2633)
18962306a36Sopenharmony_ci		printk(KERN_CONT "D2633 ");
19062306a36Sopenharmony_ci	if (type & DTV6)
19162306a36Sopenharmony_ci		printk(KERN_CONT "DTV6 ");
19262306a36Sopenharmony_ci	if (type & QAM)
19362306a36Sopenharmony_ci		printk(KERN_CONT "QAM ");
19462306a36Sopenharmony_ci	if (type & DTV7)
19562306a36Sopenharmony_ci		printk(KERN_CONT "DTV7 ");
19662306a36Sopenharmony_ci	if (type & DTV78)
19762306a36Sopenharmony_ci		printk(KERN_CONT "DTV78 ");
19862306a36Sopenharmony_ci	if (type & DTV8)
19962306a36Sopenharmony_ci		printk(KERN_CONT "DTV8 ");
20062306a36Sopenharmony_ci	if (type & FM)
20162306a36Sopenharmony_ci		printk(KERN_CONT "FM ");
20262306a36Sopenharmony_ci	if (type & INPUT1)
20362306a36Sopenharmony_ci		printk(KERN_CONT "INPUT1 ");
20462306a36Sopenharmony_ci	if (type & LCD)
20562306a36Sopenharmony_ci		printk(KERN_CONT "LCD ");
20662306a36Sopenharmony_ci	if (type & NOGD)
20762306a36Sopenharmony_ci		printk(KERN_CONT "NOGD ");
20862306a36Sopenharmony_ci	if (type & MONO)
20962306a36Sopenharmony_ci		printk(KERN_CONT "MONO ");
21062306a36Sopenharmony_ci	if (type & ATSC)
21162306a36Sopenharmony_ci		printk(KERN_CONT "ATSC ");
21262306a36Sopenharmony_ci	if (type & IF)
21362306a36Sopenharmony_ci		printk(KERN_CONT "IF ");
21462306a36Sopenharmony_ci	if (type & LG60)
21562306a36Sopenharmony_ci		printk(KERN_CONT "LG60 ");
21662306a36Sopenharmony_ci	if (type & ATI638)
21762306a36Sopenharmony_ci		printk(KERN_CONT "ATI638 ");
21862306a36Sopenharmony_ci	if (type & OREN538)
21962306a36Sopenharmony_ci		printk(KERN_CONT "OREN538 ");
22062306a36Sopenharmony_ci	if (type & OREN36)
22162306a36Sopenharmony_ci		printk(KERN_CONT "OREN36 ");
22262306a36Sopenharmony_ci	if (type & TOYOTA388)
22362306a36Sopenharmony_ci		printk(KERN_CONT "TOYOTA388 ");
22462306a36Sopenharmony_ci	if (type & TOYOTA794)
22562306a36Sopenharmony_ci		printk(KERN_CONT "TOYOTA794 ");
22662306a36Sopenharmony_ci	if (type & DIBCOM52)
22762306a36Sopenharmony_ci		printk(KERN_CONT "DIBCOM52 ");
22862306a36Sopenharmony_ci	if (type & ZARLINK456)
22962306a36Sopenharmony_ci		printk(KERN_CONT "ZARLINK456 ");
23062306a36Sopenharmony_ci	if (type & CHINA)
23162306a36Sopenharmony_ci		printk(KERN_CONT "CHINA ");
23262306a36Sopenharmony_ci	if (type & F6MHZ)
23362306a36Sopenharmony_ci		printk(KERN_CONT "F6MHZ ");
23462306a36Sopenharmony_ci	if (type & INPUT2)
23562306a36Sopenharmony_ci		printk(KERN_CONT "INPUT2 ");
23662306a36Sopenharmony_ci	if (type & SCODE)
23762306a36Sopenharmony_ci		printk(KERN_CONT "SCODE ");
23862306a36Sopenharmony_ci	if (type & HAS_IF)
23962306a36Sopenharmony_ci		printk(KERN_CONT "HAS_IF_%d ", int_freq);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic  v4l2_std_id parse_audio_std_option(void)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	if (strcasecmp(audio_std, "A2") == 0)
24562306a36Sopenharmony_ci		return V4L2_STD_A2;
24662306a36Sopenharmony_ci	if (strcasecmp(audio_std, "A2/A") == 0)
24762306a36Sopenharmony_ci		return V4L2_STD_A2_A;
24862306a36Sopenharmony_ci	if (strcasecmp(audio_std, "A2/B") == 0)
24962306a36Sopenharmony_ci		return V4L2_STD_A2_B;
25062306a36Sopenharmony_ci	if (strcasecmp(audio_std, "NICAM") == 0)
25162306a36Sopenharmony_ci		return V4L2_STD_NICAM;
25262306a36Sopenharmony_ci	if (strcasecmp(audio_std, "NICAM/A") == 0)
25362306a36Sopenharmony_ci		return V4L2_STD_NICAM_A;
25462306a36Sopenharmony_ci	if (strcasecmp(audio_std, "NICAM/B") == 0)
25562306a36Sopenharmony_ci		return V4L2_STD_NICAM_B;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int check_device_status(struct xc2028_data *priv)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	switch (priv->state) {
26362306a36Sopenharmony_ci	case XC2028_NO_FIRMWARE:
26462306a36Sopenharmony_ci	case XC2028_WAITING_FIRMWARE:
26562306a36Sopenharmony_ci		return -EAGAIN;
26662306a36Sopenharmony_ci	case XC2028_ACTIVE:
26762306a36Sopenharmony_ci		return 1;
26862306a36Sopenharmony_ci	case XC2028_SLEEP:
26962306a36Sopenharmony_ci		return 0;
27062306a36Sopenharmony_ci	case XC2028_NODEV:
27162306a36Sopenharmony_ci		return -ENODEV;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci	return 0;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic void free_firmware(struct xc2028_data *priv)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	int i;
27962306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* free allocated f/w string */
28262306a36Sopenharmony_ci	if (priv->fname != firmware_name)
28362306a36Sopenharmony_ci		kfree(priv->fname);
28462306a36Sopenharmony_ci	priv->fname = NULL;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	priv->state = XC2028_NO_FIRMWARE;
28762306a36Sopenharmony_ci	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (!priv->firm)
29062306a36Sopenharmony_ci		return;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	for (i = 0; i < priv->firm_size; i++)
29362306a36Sopenharmony_ci		kfree(priv->firm[i].ptr);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	kfree(priv->firm);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	priv->firm = NULL;
29862306a36Sopenharmony_ci	priv->firm_size = 0;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int load_all_firmwares(struct dvb_frontend *fe,
30262306a36Sopenharmony_ci			      const struct firmware *fw)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct xc2028_data    *priv = fe->tuner_priv;
30562306a36Sopenharmony_ci	const unsigned char   *p, *endp;
30662306a36Sopenharmony_ci	int                   rc = 0;
30762306a36Sopenharmony_ci	int		      n, n_array;
30862306a36Sopenharmony_ci	char		      name[33];
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	p = fw->data;
31362306a36Sopenharmony_ci	endp = p + fw->size;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (fw->size < sizeof(name) - 1 + 2 + 2) {
31662306a36Sopenharmony_ci		tuner_err("Error: firmware file %s has invalid size!\n",
31762306a36Sopenharmony_ci			  priv->fname);
31862306a36Sopenharmony_ci		goto corrupt;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	memcpy(name, p, sizeof(name) - 1);
32262306a36Sopenharmony_ci	name[sizeof(name) - 1] = 0;
32362306a36Sopenharmony_ci	p += sizeof(name) - 1;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	priv->firm_version = get_unaligned_le16(p);
32662306a36Sopenharmony_ci	p += 2;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	n_array = get_unaligned_le16(p);
32962306a36Sopenharmony_ci	p += 2;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
33262306a36Sopenharmony_ci		   n_array, priv->fname, name,
33362306a36Sopenharmony_ci		   priv->firm_version >> 8, priv->firm_version & 0xff);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL);
33662306a36Sopenharmony_ci	if (priv->firm == NULL) {
33762306a36Sopenharmony_ci		tuner_err("Not enough memory to load firmware file.\n");
33862306a36Sopenharmony_ci		rc = -ENOMEM;
33962306a36Sopenharmony_ci		goto err;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci	priv->firm_size = n_array;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	n = -1;
34462306a36Sopenharmony_ci	while (p < endp) {
34562306a36Sopenharmony_ci		__u32 type, size;
34662306a36Sopenharmony_ci		v4l2_std_id id;
34762306a36Sopenharmony_ci		__u16 int_freq = 0;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		n++;
35062306a36Sopenharmony_ci		if (n >= n_array) {
35162306a36Sopenharmony_ci			tuner_err("More firmware images in file than were expected!\n");
35262306a36Sopenharmony_ci			goto corrupt;
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		/* Checks if there's enough bytes to read */
35662306a36Sopenharmony_ci		if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))
35762306a36Sopenharmony_ci			goto header;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		type = get_unaligned_le32(p);
36062306a36Sopenharmony_ci		p += sizeof(type);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		id = get_unaligned_le64(p);
36362306a36Sopenharmony_ci		p += sizeof(id);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		if (type & HAS_IF) {
36662306a36Sopenharmony_ci			int_freq = get_unaligned_le16(p);
36762306a36Sopenharmony_ci			p += sizeof(int_freq);
36862306a36Sopenharmony_ci			if (endp - p < sizeof(size))
36962306a36Sopenharmony_ci				goto header;
37062306a36Sopenharmony_ci		}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		size = get_unaligned_le32(p);
37362306a36Sopenharmony_ci		p += sizeof(size);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci		if (!size || size > endp - p) {
37662306a36Sopenharmony_ci			tuner_err("Firmware type ");
37762306a36Sopenharmony_ci			dump_firm_type(type);
37862306a36Sopenharmony_ci			printk(KERN_CONT
37962306a36Sopenharmony_ci			       "(%x), id %llx is corrupted (size=%zd, expected %d)\n",
38062306a36Sopenharmony_ci			       type, (unsigned long long)id, (endp - p), size);
38162306a36Sopenharmony_ci			goto corrupt;
38262306a36Sopenharmony_ci		}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		priv->firm[n].ptr = kmemdup(p, size, GFP_KERNEL);
38562306a36Sopenharmony_ci		if (priv->firm[n].ptr == NULL) {
38662306a36Sopenharmony_ci			tuner_err("Not enough memory to load firmware file.\n");
38762306a36Sopenharmony_ci			rc = -ENOMEM;
38862306a36Sopenharmony_ci			goto err;
38962306a36Sopenharmony_ci		}
39062306a36Sopenharmony_ci		tuner_dbg("Reading firmware type ");
39162306a36Sopenharmony_ci		if (debug) {
39262306a36Sopenharmony_ci			dump_firm_type_and_int_freq(type, int_freq);
39362306a36Sopenharmony_ci			printk(KERN_CONT "(%x), id %llx, size=%d.\n",
39462306a36Sopenharmony_ci			       type, (unsigned long long)id, size);
39562306a36Sopenharmony_ci		}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		priv->firm[n].type = type;
39862306a36Sopenharmony_ci		priv->firm[n].id   = id;
39962306a36Sopenharmony_ci		priv->firm[n].size = size;
40062306a36Sopenharmony_ci		priv->firm[n].int_freq = int_freq;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		p += size;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (n + 1 != priv->firm_size) {
40662306a36Sopenharmony_ci		tuner_err("Firmware file is incomplete!\n");
40762306a36Sopenharmony_ci		goto corrupt;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	goto done;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ciheader:
41362306a36Sopenharmony_ci	tuner_err("Firmware header is incomplete!\n");
41462306a36Sopenharmony_cicorrupt:
41562306a36Sopenharmony_ci	rc = -EINVAL;
41662306a36Sopenharmony_ci	tuner_err("Error: firmware file is corrupted!\n");
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cierr:
41962306a36Sopenharmony_ci	tuner_info("Releasing partially loaded firmware file.\n");
42062306a36Sopenharmony_ci	free_firmware(priv);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cidone:
42362306a36Sopenharmony_ci	if (rc == 0)
42462306a36Sopenharmony_ci		tuner_dbg("Firmware files loaded.\n");
42562306a36Sopenharmony_ci	else
42662306a36Sopenharmony_ci		priv->state = XC2028_NODEV;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	return rc;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int seek_firmware(struct dvb_frontend *fe, unsigned int type,
43262306a36Sopenharmony_ci			 v4l2_std_id *id)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
43562306a36Sopenharmony_ci	int                 i, best_i = -1, best_nr_matches = 0;
43662306a36Sopenharmony_ci	unsigned int        type_mask = 0;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	tuner_dbg("%s called, want type=", __func__);
43962306a36Sopenharmony_ci	if (debug) {
44062306a36Sopenharmony_ci		dump_firm_type(type);
44162306a36Sopenharmony_ci		printk(KERN_CONT "(%x), id %016llx.\n",
44262306a36Sopenharmony_ci		       type, (unsigned long long)*id);
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (!priv->firm) {
44662306a36Sopenharmony_ci		tuner_err("Error! firmware not loaded\n");
44762306a36Sopenharmony_ci		return -EINVAL;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (((type & ~SCODE) == 0) && (*id == 0))
45162306a36Sopenharmony_ci		*id = V4L2_STD_PAL;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (type & BASE)
45462306a36Sopenharmony_ci		type_mask = BASE_TYPES;
45562306a36Sopenharmony_ci	else if (type & SCODE) {
45662306a36Sopenharmony_ci		type &= SCODE_TYPES;
45762306a36Sopenharmony_ci		type_mask = SCODE_TYPES & ~HAS_IF;
45862306a36Sopenharmony_ci	} else if (type & DTV_TYPES)
45962306a36Sopenharmony_ci		type_mask = DTV_TYPES;
46062306a36Sopenharmony_ci	else if (type & STD_SPECIFIC_TYPES)
46162306a36Sopenharmony_ci		type_mask = STD_SPECIFIC_TYPES;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	type &= type_mask;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (!(type & SCODE))
46662306a36Sopenharmony_ci		type_mask = ~0;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* Seek for exact match */
46962306a36Sopenharmony_ci	for (i = 0; i < priv->firm_size; i++) {
47062306a36Sopenharmony_ci		if ((type == (priv->firm[i].type & type_mask)) &&
47162306a36Sopenharmony_ci		    (*id == priv->firm[i].id))
47262306a36Sopenharmony_ci			goto found;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* Seek for generic video standard match */
47662306a36Sopenharmony_ci	for (i = 0; i < priv->firm_size; i++) {
47762306a36Sopenharmony_ci		v4l2_std_id match_mask;
47862306a36Sopenharmony_ci		int nr_matches;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		if (type != (priv->firm[i].type & type_mask))
48162306a36Sopenharmony_ci			continue;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		match_mask = *id & priv->firm[i].id;
48462306a36Sopenharmony_ci		if (!match_mask)
48562306a36Sopenharmony_ci			continue;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		if ((*id & match_mask) == *id)
48862306a36Sopenharmony_ci			goto found; /* Supports all the requested standards */
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		nr_matches = hweight64(match_mask);
49162306a36Sopenharmony_ci		if (nr_matches > best_nr_matches) {
49262306a36Sopenharmony_ci			best_nr_matches = nr_matches;
49362306a36Sopenharmony_ci			best_i = i;
49462306a36Sopenharmony_ci		}
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (best_nr_matches > 0) {
49862306a36Sopenharmony_ci		tuner_dbg("Selecting best matching firmware (%d bits) for type=",
49962306a36Sopenharmony_ci			  best_nr_matches);
50062306a36Sopenharmony_ci		dump_firm_type(type);
50162306a36Sopenharmony_ci		printk(KERN_CONT
50262306a36Sopenharmony_ci		       "(%x), id %016llx:\n", type, (unsigned long long)*id);
50362306a36Sopenharmony_ci		i = best_i;
50462306a36Sopenharmony_ci		goto found;
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	/*FIXME: Would make sense to seek for type "hint" match ? */
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	i = -ENOENT;
51062306a36Sopenharmony_ci	goto ret;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cifound:
51362306a36Sopenharmony_ci	*id = priv->firm[i].id;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ciret:
51662306a36Sopenharmony_ci	tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found");
51762306a36Sopenharmony_ci	if (debug) {
51862306a36Sopenharmony_ci		dump_firm_type(type);
51962306a36Sopenharmony_ci		printk(KERN_CONT "(%x), id %016llx.\n",
52062306a36Sopenharmony_ci		       type, (unsigned long long)*id);
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci	return i;
52362306a36Sopenharmony_ci}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_cistatic inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/* analog side (tuner-core) uses i2c_adap->algo_data.
53062306a36Sopenharmony_ci	 * digital side is not guaranteed to have algo_data defined.
53162306a36Sopenharmony_ci	 *
53262306a36Sopenharmony_ci	 * digital side will always have fe->dvb defined.
53362306a36Sopenharmony_ci	 * analog side (tuner-core) doesn't (yet) define fe->dvb.
53462306a36Sopenharmony_ci	 */
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return (!fe->callback) ? -EINVAL :
53762306a36Sopenharmony_ci		fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
53862306a36Sopenharmony_ci				fe->dvb->priv : priv->i2c_props.adap->algo_data,
53962306a36Sopenharmony_ci			     DVB_FRONTEND_COMPONENT_TUNER, cmd, arg);
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic int load_firmware(struct dvb_frontend *fe, unsigned int type,
54362306a36Sopenharmony_ci			 v4l2_std_id *id)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
54662306a36Sopenharmony_ci	int                pos, rc;
54762306a36Sopenharmony_ci	unsigned char      *p, *endp, buf[MAX_XFER_SIZE];
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (priv->ctrl.max_len > sizeof(buf))
55062306a36Sopenharmony_ci		priv->ctrl.max_len = sizeof(buf);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	pos = seek_firmware(fe, type, id);
55562306a36Sopenharmony_ci	if (pos < 0)
55662306a36Sopenharmony_ci		return pos;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	tuner_info("Loading firmware for type=");
55962306a36Sopenharmony_ci	dump_firm_type(priv->firm[pos].type);
56062306a36Sopenharmony_ci	printk(KERN_CONT "(%x), id %016llx.\n",
56162306a36Sopenharmony_ci	       priv->firm[pos].type, (unsigned long long)*id);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	p = priv->firm[pos].ptr;
56462306a36Sopenharmony_ci	endp = p + priv->firm[pos].size;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	while (p < endp) {
56762306a36Sopenharmony_ci		__u16 size;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		/* Checks if there's enough bytes to read */
57062306a36Sopenharmony_ci		if (p + sizeof(size) > endp) {
57162306a36Sopenharmony_ci			tuner_err("Firmware chunk size is wrong\n");
57262306a36Sopenharmony_ci			return -EINVAL;
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		size = le16_to_cpu(*(__le16 *) p);
57662306a36Sopenharmony_ci		p += sizeof(size);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		if (size == 0xffff)
57962306a36Sopenharmony_ci			return 0;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		if (!size) {
58262306a36Sopenharmony_ci			/* Special callback command received */
58362306a36Sopenharmony_ci			rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
58462306a36Sopenharmony_ci			if (rc < 0) {
58562306a36Sopenharmony_ci				tuner_err("Error at RESET code %d\n",
58662306a36Sopenharmony_ci					   (*p) & 0x7f);
58762306a36Sopenharmony_ci				return -EINVAL;
58862306a36Sopenharmony_ci			}
58962306a36Sopenharmony_ci			continue;
59062306a36Sopenharmony_ci		}
59162306a36Sopenharmony_ci		if (size >= 0xff00) {
59262306a36Sopenharmony_ci			switch (size) {
59362306a36Sopenharmony_ci			case 0xff00:
59462306a36Sopenharmony_ci				rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0);
59562306a36Sopenharmony_ci				if (rc < 0) {
59662306a36Sopenharmony_ci					tuner_err("Error at RESET code %d\n",
59762306a36Sopenharmony_ci						  (*p) & 0x7f);
59862306a36Sopenharmony_ci					return -EINVAL;
59962306a36Sopenharmony_ci				}
60062306a36Sopenharmony_ci				break;
60162306a36Sopenharmony_ci			default:
60262306a36Sopenharmony_ci				tuner_info("Invalid RESET code %d\n",
60362306a36Sopenharmony_ci					   size & 0x7f);
60462306a36Sopenharmony_ci				return -EINVAL;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci			}
60762306a36Sopenharmony_ci			continue;
60862306a36Sopenharmony_ci		}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		/* Checks for a sleep command */
61162306a36Sopenharmony_ci		if (size & 0x8000) {
61262306a36Sopenharmony_ci			msleep(size & 0x7fff);
61362306a36Sopenharmony_ci			continue;
61462306a36Sopenharmony_ci		}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci		if ((size + p > endp)) {
61762306a36Sopenharmony_ci			tuner_err("missing bytes: need %d, have %zd\n",
61862306a36Sopenharmony_ci				   size, (endp - p));
61962306a36Sopenharmony_ci			return -EINVAL;
62062306a36Sopenharmony_ci		}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci		buf[0] = *p;
62362306a36Sopenharmony_ci		p++;
62462306a36Sopenharmony_ci		size--;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		/* Sends message chunks */
62762306a36Sopenharmony_ci		while (size > 0) {
62862306a36Sopenharmony_ci			int len = (size < priv->ctrl.max_len - 1) ?
62962306a36Sopenharmony_ci				   size : priv->ctrl.max_len - 1;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci			memcpy(buf + 1, p, len);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci			rc = i2c_send(priv, buf, len + 1);
63462306a36Sopenharmony_ci			if (rc < 0) {
63562306a36Sopenharmony_ci				tuner_err("%d returned from send\n", rc);
63662306a36Sopenharmony_ci				return -EINVAL;
63762306a36Sopenharmony_ci			}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci			p += len;
64062306a36Sopenharmony_ci			size -= len;
64162306a36Sopenharmony_ci		}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		/* silently fail if the frontend doesn't support I2C flush */
64462306a36Sopenharmony_ci		rc = do_tuner_callback(fe, XC2028_I2C_FLUSH, 0);
64562306a36Sopenharmony_ci		if ((rc < 0) && (rc != -EINVAL)) {
64662306a36Sopenharmony_ci			tuner_err("error executing flush: %d\n", rc);
64762306a36Sopenharmony_ci			return rc;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci	return 0;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic int load_scode(struct dvb_frontend *fe, unsigned int type,
65462306a36Sopenharmony_ci			 v4l2_std_id *id, __u16 int_freq, int scode)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
65762306a36Sopenharmony_ci	int                pos, rc;
65862306a36Sopenharmony_ci	unsigned char	   *p;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (!int_freq) {
66362306a36Sopenharmony_ci		pos = seek_firmware(fe, type, id);
66462306a36Sopenharmony_ci		if (pos < 0)
66562306a36Sopenharmony_ci			return pos;
66662306a36Sopenharmony_ci	} else {
66762306a36Sopenharmony_ci		for (pos = 0; pos < priv->firm_size; pos++) {
66862306a36Sopenharmony_ci			if ((priv->firm[pos].int_freq == int_freq) &&
66962306a36Sopenharmony_ci			    (priv->firm[pos].type & HAS_IF))
67062306a36Sopenharmony_ci				break;
67162306a36Sopenharmony_ci		}
67262306a36Sopenharmony_ci		if (pos == priv->firm_size)
67362306a36Sopenharmony_ci			return -ENOENT;
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	p = priv->firm[pos].ptr;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (priv->firm[pos].type & HAS_IF) {
67962306a36Sopenharmony_ci		if (priv->firm[pos].size != 12 * 16 || scode >= 16)
68062306a36Sopenharmony_ci			return -EINVAL;
68162306a36Sopenharmony_ci		p += 12 * scode;
68262306a36Sopenharmony_ci	} else {
68362306a36Sopenharmony_ci		/* 16 SCODE entries per file; each SCODE entry is 12 bytes and
68462306a36Sopenharmony_ci		 * has a 2-byte size header in the firmware format. */
68562306a36Sopenharmony_ci		if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||
68662306a36Sopenharmony_ci		    le16_to_cpu(*(__le16 *)(p + 14 * scode)) != 12)
68762306a36Sopenharmony_ci			return -EINVAL;
68862306a36Sopenharmony_ci		p += 14 * scode + 2;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	tuner_info("Loading SCODE for type=");
69262306a36Sopenharmony_ci	dump_firm_type_and_int_freq(priv->firm[pos].type,
69362306a36Sopenharmony_ci				    priv->firm[pos].int_freq);
69462306a36Sopenharmony_ci	printk(KERN_CONT "(%x), id %016llx.\n", priv->firm[pos].type,
69562306a36Sopenharmony_ci	       (unsigned long long)*id);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (priv->firm_version < 0x0202)
69862306a36Sopenharmony_ci		rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00});
69962306a36Sopenharmony_ci	else
70062306a36Sopenharmony_ci		rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00});
70162306a36Sopenharmony_ci	if (rc < 0)
70262306a36Sopenharmony_ci		return -EIO;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	rc = i2c_send(priv, p, 12);
70562306a36Sopenharmony_ci	if (rc < 0)
70662306a36Sopenharmony_ci		return -EIO;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	rc = send_seq(priv, {0x00, 0x8c});
70962306a36Sopenharmony_ci	if (rc < 0)
71062306a36Sopenharmony_ci		return -EIO;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return 0;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic int xc2028_sleep(struct dvb_frontend *fe);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic int check_firmware(struct dvb_frontend *fe, unsigned int type,
71862306a36Sopenharmony_ci			  v4l2_std_id std, __u16 int_freq)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	struct xc2028_data         *priv = fe->tuner_priv;
72162306a36Sopenharmony_ci	struct firmware_properties new_fw;
72262306a36Sopenharmony_ci	int			   rc, retry_count = 0;
72362306a36Sopenharmony_ci	u16			   version, hwmodel;
72462306a36Sopenharmony_ci	v4l2_std_id		   std0;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	rc = check_device_status(priv);
72962306a36Sopenharmony_ci	if (rc < 0)
73062306a36Sopenharmony_ci		return rc;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (priv->ctrl.mts && !(type & FM))
73362306a36Sopenharmony_ci		type |= MTS;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ciretry:
73662306a36Sopenharmony_ci	new_fw.type = type;
73762306a36Sopenharmony_ci	new_fw.id = std;
73862306a36Sopenharmony_ci	new_fw.std_req = std;
73962306a36Sopenharmony_ci	new_fw.scode_table = SCODE | priv->ctrl.scode_table;
74062306a36Sopenharmony_ci	new_fw.scode_nr = 0;
74162306a36Sopenharmony_ci	new_fw.int_freq = int_freq;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	tuner_dbg("checking firmware, user requested type=");
74462306a36Sopenharmony_ci	if (debug) {
74562306a36Sopenharmony_ci		dump_firm_type(new_fw.type);
74662306a36Sopenharmony_ci		printk(KERN_CONT "(%x), id %016llx, ", new_fw.type,
74762306a36Sopenharmony_ci		       (unsigned long long)new_fw.std_req);
74862306a36Sopenharmony_ci		if (!int_freq) {
74962306a36Sopenharmony_ci			printk(KERN_CONT "scode_tbl ");
75062306a36Sopenharmony_ci			dump_firm_type(priv->ctrl.scode_table);
75162306a36Sopenharmony_ci			printk(KERN_CONT "(%x), ", priv->ctrl.scode_table);
75262306a36Sopenharmony_ci		} else
75362306a36Sopenharmony_ci			printk(KERN_CONT "int_freq %d, ", new_fw.int_freq);
75462306a36Sopenharmony_ci		printk(KERN_CONT "scode_nr %d\n", new_fw.scode_nr);
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/*
75862306a36Sopenharmony_ci	 * No need to reload base firmware if it matches and if the tuner
75962306a36Sopenharmony_ci	 * is not at sleep mode
76062306a36Sopenharmony_ci	 */
76162306a36Sopenharmony_ci	if ((priv->state == XC2028_ACTIVE) &&
76262306a36Sopenharmony_ci	    (((BASE | new_fw.type) & BASE_TYPES) ==
76362306a36Sopenharmony_ci	    (priv->cur_fw.type & BASE_TYPES))) {
76462306a36Sopenharmony_ci		tuner_dbg("BASE firmware not changed.\n");
76562306a36Sopenharmony_ci		goto skip_base;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/* Updating BASE - forget about all currently loaded firmware */
76962306a36Sopenharmony_ci	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	/* Reset is needed before loading firmware */
77262306a36Sopenharmony_ci	rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
77362306a36Sopenharmony_ci	if (rc < 0)
77462306a36Sopenharmony_ci		goto fail;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	/* BASE firmwares are all std0 */
77762306a36Sopenharmony_ci	std0 = 0;
77862306a36Sopenharmony_ci	rc = load_firmware(fe, BASE | new_fw.type, &std0);
77962306a36Sopenharmony_ci	if (rc < 0) {
78062306a36Sopenharmony_ci		tuner_err("Error %d while loading base firmware\n",
78162306a36Sopenharmony_ci			  rc);
78262306a36Sopenharmony_ci		goto fail;
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	/* Load INIT1, if needed */
78662306a36Sopenharmony_ci	tuner_dbg("Load init1 firmware, if exists\n");
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0);
78962306a36Sopenharmony_ci	if (rc == -ENOENT)
79062306a36Sopenharmony_ci		rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ,
79162306a36Sopenharmony_ci				   &std0);
79262306a36Sopenharmony_ci	if (rc < 0 && rc != -ENOENT) {
79362306a36Sopenharmony_ci		tuner_err("Error %d while loading init1 firmware\n",
79462306a36Sopenharmony_ci			  rc);
79562306a36Sopenharmony_ci		goto fail;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ciskip_base:
79962306a36Sopenharmony_ci	/*
80062306a36Sopenharmony_ci	 * No need to reload standard specific firmware if base firmware
80162306a36Sopenharmony_ci	 * was not reloaded and requested video standards have not changed.
80262306a36Sopenharmony_ci	 */
80362306a36Sopenharmony_ci	if (priv->cur_fw.type == (BASE | new_fw.type) &&
80462306a36Sopenharmony_ci	    priv->cur_fw.std_req == std) {
80562306a36Sopenharmony_ci		tuner_dbg("Std-specific firmware already loaded.\n");
80662306a36Sopenharmony_ci		goto skip_std_specific;
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	/* Reloading std-specific firmware forces a SCODE update */
81062306a36Sopenharmony_ci	priv->cur_fw.scode_table = 0;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	rc = load_firmware(fe, new_fw.type, &new_fw.id);
81362306a36Sopenharmony_ci	if (rc == -ENOENT)
81462306a36Sopenharmony_ci		rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (rc < 0)
81762306a36Sopenharmony_ci		goto fail;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ciskip_std_specific:
82062306a36Sopenharmony_ci	if (priv->cur_fw.scode_table == new_fw.scode_table &&
82162306a36Sopenharmony_ci	    priv->cur_fw.scode_nr == new_fw.scode_nr) {
82262306a36Sopenharmony_ci		tuner_dbg("SCODE firmware already loaded.\n");
82362306a36Sopenharmony_ci		goto check_device;
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (new_fw.type & FM)
82762306a36Sopenharmony_ci		goto check_device;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	/* Load SCODE firmware, if exists */
83062306a36Sopenharmony_ci	tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
83362306a36Sopenharmony_ci			new_fw.int_freq, new_fw.scode_nr);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cicheck_device:
83662306a36Sopenharmony_ci	if (xc2028_get_reg(priv, 0x0004, &version) < 0 ||
83762306a36Sopenharmony_ci	    xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) {
83862306a36Sopenharmony_ci		tuner_err("Unable to read tuner registers.\n");
83962306a36Sopenharmony_ci		goto fail;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	tuner_dbg("Device is Xceive %d version %d.%d, firmware version %d.%d\n",
84362306a36Sopenharmony_ci		  hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
84462306a36Sopenharmony_ci		  (version & 0xf0) >> 4, version & 0xf);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (priv->ctrl.read_not_reliable)
84862306a36Sopenharmony_ci		goto read_not_reliable;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	/* Check firmware version against what we downloaded. */
85162306a36Sopenharmony_ci	if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) {
85262306a36Sopenharmony_ci		if (!priv->ctrl.read_not_reliable) {
85362306a36Sopenharmony_ci			tuner_err("Incorrect readback of firmware version.\n");
85462306a36Sopenharmony_ci			goto fail;
85562306a36Sopenharmony_ci		} else {
85662306a36Sopenharmony_ci			tuner_err("Returned an incorrect version. However, read is not reliable enough. Ignoring it.\n");
85762306a36Sopenharmony_ci			hwmodel = 3028;
85862306a36Sopenharmony_ci		}
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	/* Check that the tuner hardware model remains consistent over time. */
86262306a36Sopenharmony_ci	if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) {
86362306a36Sopenharmony_ci		priv->hwmodel = hwmodel;
86462306a36Sopenharmony_ci		priv->hwvers  = version & 0xff00;
86562306a36Sopenharmony_ci	} else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
86662306a36Sopenharmony_ci		   priv->hwvers != (version & 0xff00)) {
86762306a36Sopenharmony_ci		tuner_err("Read invalid device hardware information - tuner hung?\n");
86862306a36Sopenharmony_ci		goto fail;
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ciread_not_reliable:
87262306a36Sopenharmony_ci	priv->cur_fw = new_fw;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	/*
87562306a36Sopenharmony_ci	 * By setting BASE in cur_fw.type only after successfully loading all
87662306a36Sopenharmony_ci	 * firmwares, we can:
87762306a36Sopenharmony_ci	 * 1. Identify that BASE firmware with type=0 has been loaded;
87862306a36Sopenharmony_ci	 * 2. Tell whether BASE firmware was just changed the next time through.
87962306a36Sopenharmony_ci	 */
88062306a36Sopenharmony_ci	priv->cur_fw.type |= BASE;
88162306a36Sopenharmony_ci	priv->state = XC2028_ACTIVE;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	return 0;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_cifail:
88662306a36Sopenharmony_ci	free_firmware(priv);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (retry_count < 8) {
88962306a36Sopenharmony_ci		msleep(50);
89062306a36Sopenharmony_ci		retry_count++;
89162306a36Sopenharmony_ci		tuner_dbg("Retrying firmware load\n");
89262306a36Sopenharmony_ci		goto retry;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	/* Firmware didn't load. Put the device to sleep */
89662306a36Sopenharmony_ci	xc2028_sleep(fe);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (rc == -ENOENT)
89962306a36Sopenharmony_ci		rc = -EINVAL;
90062306a36Sopenharmony_ci	return rc;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
90662306a36Sopenharmony_ci	u16                 frq_lock, signal = 0;
90762306a36Sopenharmony_ci	int                 rc, i;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	rc = check_device_status(priv);
91262306a36Sopenharmony_ci	if (rc < 0)
91362306a36Sopenharmony_ci		return rc;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* If the device is sleeping, no channel is tuned */
91662306a36Sopenharmony_ci	if (!rc) {
91762306a36Sopenharmony_ci		*strength = 0;
91862306a36Sopenharmony_ci		return 0;
91962306a36Sopenharmony_ci	}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	mutex_lock(&priv->lock);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	/* Sync Lock Indicator */
92462306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
92562306a36Sopenharmony_ci		rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
92662306a36Sopenharmony_ci		if (rc < 0)
92762306a36Sopenharmony_ci			goto ret;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci		if (frq_lock)
93062306a36Sopenharmony_ci			break;
93162306a36Sopenharmony_ci		msleep(6);
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	/* Frequency didn't lock */
93562306a36Sopenharmony_ci	if (frq_lock == 2)
93662306a36Sopenharmony_ci		goto ret;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	/* Get SNR of the video signal */
93962306a36Sopenharmony_ci	rc = xc2028_get_reg(priv, XREG_SNR, &signal);
94062306a36Sopenharmony_ci	if (rc < 0)
94162306a36Sopenharmony_ci		goto ret;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/* Signal level is 3 bits only */
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	signal = ((1 << 12) - 1) | ((signal & 0x07) << 12);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ciret:
94862306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	*strength = signal;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	tuner_dbg("signal strength is %d\n", signal);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	return rc;
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_cistatic int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
96062306a36Sopenharmony_ci	int i, rc;
96162306a36Sopenharmony_ci	u16 frq_lock = 0;
96262306a36Sopenharmony_ci	s16 afc_reg = 0;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	rc = check_device_status(priv);
96562306a36Sopenharmony_ci	if (rc < 0)
96662306a36Sopenharmony_ci		return rc;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	/* If the device is sleeping, no channel is tuned */
96962306a36Sopenharmony_ci	if (!rc) {
97062306a36Sopenharmony_ci		*afc = 0;
97162306a36Sopenharmony_ci		return 0;
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	mutex_lock(&priv->lock);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	/* Sync Lock Indicator */
97762306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
97862306a36Sopenharmony_ci		rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
97962306a36Sopenharmony_ci		if (rc < 0)
98062306a36Sopenharmony_ci			goto ret;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		if (frq_lock)
98362306a36Sopenharmony_ci			break;
98462306a36Sopenharmony_ci		msleep(6);
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	/* Frequency didn't lock */
98862306a36Sopenharmony_ci	if (frq_lock == 2)
98962306a36Sopenharmony_ci		goto ret;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	/* Get AFC */
99262306a36Sopenharmony_ci	rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg);
99362306a36Sopenharmony_ci	if (rc < 0)
99462306a36Sopenharmony_ci		goto ret;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	*afc = afc_reg * 15625; /* Hz */
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	tuner_dbg("AFC is %d Hz\n", *afc);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ciret:
100162306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	return rc;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci#define DIV 15625
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
100962306a36Sopenharmony_ci			    enum v4l2_tuner_type new_type,
101062306a36Sopenharmony_ci			    unsigned int type,
101162306a36Sopenharmony_ci			    v4l2_std_id std,
101262306a36Sopenharmony_ci			    u16 int_freq)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
101562306a36Sopenharmony_ci	int		   rc = -EINVAL;
101662306a36Sopenharmony_ci	unsigned char	   buf[4];
101762306a36Sopenharmony_ci	u32		   div, offset = 0;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	mutex_lock(&priv->lock);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	tuner_dbg("should set frequency %d kHz\n", freq / 1000);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	if (check_firmware(fe, type, std, int_freq) < 0)
102662306a36Sopenharmony_ci		goto ret;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* On some cases xc2028 can disable video output, if
102962306a36Sopenharmony_ci	 * very weak signals are received. By sending a soft
103062306a36Sopenharmony_ci	 * reset, this is re-enabled. So, it is better to always
103162306a36Sopenharmony_ci	 * send a soft reset before changing channels, to be sure
103262306a36Sopenharmony_ci	 * that xc2028 will be in a safe state.
103362306a36Sopenharmony_ci	 * Maybe this might also be needed for DTV.
103462306a36Sopenharmony_ci	 */
103562306a36Sopenharmony_ci	switch (new_type) {
103662306a36Sopenharmony_ci	case V4L2_TUNER_ANALOG_TV:
103762306a36Sopenharmony_ci		rc = send_seq(priv, {0x00, 0x00});
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci		/* Analog mode requires offset = 0 */
104062306a36Sopenharmony_ci		break;
104162306a36Sopenharmony_ci	case V4L2_TUNER_RADIO:
104262306a36Sopenharmony_ci		/* Radio mode requires offset = 0 */
104362306a36Sopenharmony_ci		break;
104462306a36Sopenharmony_ci	case V4L2_TUNER_DIGITAL_TV:
104562306a36Sopenharmony_ci		/*
104662306a36Sopenharmony_ci		 * Digital modes require an offset to adjust to the
104762306a36Sopenharmony_ci		 * proper frequency. The offset depends on what
104862306a36Sopenharmony_ci		 * firmware version is used.
104962306a36Sopenharmony_ci		 */
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci		/*
105262306a36Sopenharmony_ci		 * Adjust to the center frequency. This is calculated by the
105362306a36Sopenharmony_ci		 * formula: offset = 1.25MHz - BW/2
105462306a36Sopenharmony_ci		 * For DTV 7/8, the firmware uses BW = 8000, so it needs a
105562306a36Sopenharmony_ci		 * further adjustment to get the frequency center on VHF
105662306a36Sopenharmony_ci		 */
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci		/*
105962306a36Sopenharmony_ci		 * The firmware DTV78 used to work fine in UHF band (8 MHz
106062306a36Sopenharmony_ci		 * bandwidth) but not at all in VHF band (7 MHz bandwidth).
106162306a36Sopenharmony_ci		 * The real problem was connected to the formula used to
106262306a36Sopenharmony_ci		 * calculate the center frequency offset in VHF band.
106362306a36Sopenharmony_ci		 * In fact, removing the 500KHz adjustment fixed the problem.
106462306a36Sopenharmony_ci		 * This is coherent to what was implemented for the DTV7
106562306a36Sopenharmony_ci		 * firmware.
106662306a36Sopenharmony_ci		 * In the end, now the center frequency is the same for all 3
106762306a36Sopenharmony_ci		 * firmwares (DTV7, DTV8, DTV78) and doesn't depend on channel
106862306a36Sopenharmony_ci		 * bandwidth.
106962306a36Sopenharmony_ci		 */
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		if (priv->cur_fw.type & DTV6)
107262306a36Sopenharmony_ci			offset = 1750000;
107362306a36Sopenharmony_ci		else	/* DTV7 or DTV8 or DTV78 */
107462306a36Sopenharmony_ci			offset = 2750000;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		/*
107762306a36Sopenharmony_ci		 * xc3028 additional "magic"
107862306a36Sopenharmony_ci		 * Depending on the firmware version, it needs some adjustments
107962306a36Sopenharmony_ci		 * to properly centralize the frequency. This seems to be
108062306a36Sopenharmony_ci		 * needed to compensate the SCODE table adjustments made by
108162306a36Sopenharmony_ci		 * newer firmwares
108262306a36Sopenharmony_ci		 */
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci		/*
108562306a36Sopenharmony_ci		 * The proper adjustment would be to do it at s-code table.
108662306a36Sopenharmony_ci		 * However, this didn't work, as reported by
108762306a36Sopenharmony_ci		 * Robert Lowery <rglowery@exemail.com.au>
108862306a36Sopenharmony_ci		 */
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci#if 0
109162306a36Sopenharmony_ci		/*
109262306a36Sopenharmony_ci		 * Still need tests for XC3028L (firmware 3.2 or upper)
109362306a36Sopenharmony_ci		 * So, for now, let's just comment the per-firmware
109462306a36Sopenharmony_ci		 * version of this change. Reports with xc3028l working
109562306a36Sopenharmony_ci		 * with and without the lines below are welcome
109662306a36Sopenharmony_ci		 */
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci		if (priv->firm_version < 0x0302) {
109962306a36Sopenharmony_ci			if (priv->cur_fw.type & DTV7)
110062306a36Sopenharmony_ci				offset += 500000;
110162306a36Sopenharmony_ci		} else {
110262306a36Sopenharmony_ci			if (priv->cur_fw.type & DTV7)
110362306a36Sopenharmony_ci				offset -= 300000;
110462306a36Sopenharmony_ci			else if (type != ATSC) /* DVB @6MHz, DTV 8 and DTV 7/8 */
110562306a36Sopenharmony_ci				offset += 200000;
110662306a36Sopenharmony_ci		}
110762306a36Sopenharmony_ci#endif
110862306a36Sopenharmony_ci		break;
110962306a36Sopenharmony_ci	default:
111062306a36Sopenharmony_ci		tuner_err("Unsupported tuner type %d.\n", new_type);
111162306a36Sopenharmony_ci		break;
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	div = (freq - offset + DIV / 2) / DIV;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	/* CMD= Set frequency */
111762306a36Sopenharmony_ci	if (priv->firm_version < 0x0202)
111862306a36Sopenharmony_ci		rc = send_seq(priv, {0x00, XREG_RF_FREQ, 0x00, 0x00});
111962306a36Sopenharmony_ci	else
112062306a36Sopenharmony_ci		rc = send_seq(priv, {0x80, XREG_RF_FREQ, 0x00, 0x00});
112162306a36Sopenharmony_ci	if (rc < 0)
112262306a36Sopenharmony_ci		goto ret;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	/* Return code shouldn't be checked.
112562306a36Sopenharmony_ci	   The reset CLK is needed only with tm6000.
112662306a36Sopenharmony_ci	   Driver should work fine even if this fails.
112762306a36Sopenharmony_ci	 */
112862306a36Sopenharmony_ci	if (priv->ctrl.msleep)
112962306a36Sopenharmony_ci		msleep(priv->ctrl.msleep);
113062306a36Sopenharmony_ci	do_tuner_callback(fe, XC2028_RESET_CLK, 1);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	msleep(10);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	buf[0] = 0xff & (div >> 24);
113562306a36Sopenharmony_ci	buf[1] = 0xff & (div >> 16);
113662306a36Sopenharmony_ci	buf[2] = 0xff & (div >> 8);
113762306a36Sopenharmony_ci	buf[3] = 0xff & (div);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	rc = i2c_send(priv, buf, sizeof(buf));
114062306a36Sopenharmony_ci	if (rc < 0)
114162306a36Sopenharmony_ci		goto ret;
114262306a36Sopenharmony_ci	msleep(100);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	priv->frequency = freq;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	tuner_dbg("divisor= %*ph (freq=%d.%03d)\n", 4, buf,
114762306a36Sopenharmony_ci	       freq / 1000000, (freq % 1000000) / 1000);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	rc = 0;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ciret:
115262306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	return rc;
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_cistatic int xc2028_set_analog_freq(struct dvb_frontend *fe,
115862306a36Sopenharmony_ci			      struct analog_parameters *p)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
116162306a36Sopenharmony_ci	unsigned int       type=0;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	if (p->mode == V4L2_TUNER_RADIO) {
116662306a36Sopenharmony_ci		type |= FM;
116762306a36Sopenharmony_ci		if (priv->ctrl.input1)
116862306a36Sopenharmony_ci			type |= INPUT1;
116962306a36Sopenharmony_ci		return generic_set_freq(fe, (625l * p->frequency) / 10,
117062306a36Sopenharmony_ci				V4L2_TUNER_RADIO, type, 0, 0);
117162306a36Sopenharmony_ci	}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	/* if std is not defined, choose one */
117462306a36Sopenharmony_ci	if (!p->std)
117562306a36Sopenharmony_ci		p->std = V4L2_STD_MN;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	/* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */
117862306a36Sopenharmony_ci	if (!(p->std & V4L2_STD_MN))
117962306a36Sopenharmony_ci		type |= F8MHZ;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	/* Add audio hack to std mask */
118262306a36Sopenharmony_ci	p->std |= parse_audio_std_option();
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	return generic_set_freq(fe, 62500l * p->frequency,
118562306a36Sopenharmony_ci				V4L2_TUNER_ANALOG_TV, type, p->std, 0);
118662306a36Sopenharmony_ci}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_cistatic int xc2028_set_params(struct dvb_frontend *fe)
118962306a36Sopenharmony_ci{
119062306a36Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
119162306a36Sopenharmony_ci	u32 delsys = c->delivery_system;
119262306a36Sopenharmony_ci	u32 bw = c->bandwidth_hz;
119362306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
119462306a36Sopenharmony_ci	int rc;
119562306a36Sopenharmony_ci	unsigned int       type = 0;
119662306a36Sopenharmony_ci	u16                demod = 0;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	rc = check_device_status(priv);
120162306a36Sopenharmony_ci	if (rc < 0)
120262306a36Sopenharmony_ci		return rc;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	switch (delsys) {
120562306a36Sopenharmony_ci	case SYS_DVBT:
120662306a36Sopenharmony_ci	case SYS_DVBT2:
120762306a36Sopenharmony_ci		/*
120862306a36Sopenharmony_ci		 * The only countries with 6MHz seem to be Taiwan/Uruguay.
120962306a36Sopenharmony_ci		 * Both seem to require QAM firmware for OFDM decoding
121062306a36Sopenharmony_ci		 * Tested in Taiwan by Terry Wu <terrywu2009@gmail.com>
121162306a36Sopenharmony_ci		 */
121262306a36Sopenharmony_ci		if (bw <= 6000000)
121362306a36Sopenharmony_ci			type |= QAM;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci		switch (priv->ctrl.type) {
121662306a36Sopenharmony_ci		case XC2028_D2633:
121762306a36Sopenharmony_ci			type |= D2633;
121862306a36Sopenharmony_ci			break;
121962306a36Sopenharmony_ci		case XC2028_D2620:
122062306a36Sopenharmony_ci			type |= D2620;
122162306a36Sopenharmony_ci			break;
122262306a36Sopenharmony_ci		case XC2028_AUTO:
122362306a36Sopenharmony_ci		default:
122462306a36Sopenharmony_ci			/* Zarlink seems to need D2633 */
122562306a36Sopenharmony_ci			if (priv->ctrl.demod == XC3028_FE_ZARLINK456)
122662306a36Sopenharmony_ci				type |= D2633;
122762306a36Sopenharmony_ci			else
122862306a36Sopenharmony_ci				type |= D2620;
122962306a36Sopenharmony_ci		}
123062306a36Sopenharmony_ci		break;
123162306a36Sopenharmony_ci	case SYS_ATSC:
123262306a36Sopenharmony_ci		/* The only ATSC firmware (at least on v2.7) is D2633 */
123362306a36Sopenharmony_ci		type |= ATSC | D2633;
123462306a36Sopenharmony_ci		break;
123562306a36Sopenharmony_ci	/* DVB-S and pure QAM (FE_QAM) are not supported */
123662306a36Sopenharmony_ci	default:
123762306a36Sopenharmony_ci		return -EINVAL;
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	if (bw <= 6000000) {
124162306a36Sopenharmony_ci		type |= DTV6;
124262306a36Sopenharmony_ci		priv->ctrl.vhfbw7 = 0;
124362306a36Sopenharmony_ci		priv->ctrl.uhfbw8 = 0;
124462306a36Sopenharmony_ci	} else if (bw <= 7000000) {
124562306a36Sopenharmony_ci		if (c->frequency < 470000000)
124662306a36Sopenharmony_ci			priv->ctrl.vhfbw7 = 1;
124762306a36Sopenharmony_ci		else
124862306a36Sopenharmony_ci			priv->ctrl.uhfbw8 = 0;
124962306a36Sopenharmony_ci		type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7;
125062306a36Sopenharmony_ci		type |= F8MHZ;
125162306a36Sopenharmony_ci	} else {
125262306a36Sopenharmony_ci		if (c->frequency < 470000000)
125362306a36Sopenharmony_ci			priv->ctrl.vhfbw7 = 0;
125462306a36Sopenharmony_ci		else
125562306a36Sopenharmony_ci			priv->ctrl.uhfbw8 = 1;
125662306a36Sopenharmony_ci		type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8;
125762306a36Sopenharmony_ci		type |= F8MHZ;
125862306a36Sopenharmony_ci	}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	/* All S-code tables need a 200kHz shift */
126162306a36Sopenharmony_ci	if (priv->ctrl.demod) {
126262306a36Sopenharmony_ci		demod = priv->ctrl.demod;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci		/*
126562306a36Sopenharmony_ci		 * Newer firmwares require a 200 kHz offset only for ATSC
126662306a36Sopenharmony_ci		 */
126762306a36Sopenharmony_ci		if (type == ATSC || priv->firm_version < 0x0302)
126862306a36Sopenharmony_ci			demod += 200;
126962306a36Sopenharmony_ci		/*
127062306a36Sopenharmony_ci		 * The DTV7 S-code table needs a 700 kHz shift.
127162306a36Sopenharmony_ci		 *
127262306a36Sopenharmony_ci		 * DTV7 is only used in Australia.  Germany or Italy may also
127362306a36Sopenharmony_ci		 * use this firmware after initialization, but a tune to a UHF
127462306a36Sopenharmony_ci		 * channel should then cause DTV78 to be used.
127562306a36Sopenharmony_ci		 *
127662306a36Sopenharmony_ci		 * Unfortunately, on real-field tests, the s-code offset
127762306a36Sopenharmony_ci		 * didn't work as expected, as reported by
127862306a36Sopenharmony_ci		 * Robert Lowery <rglowery@exemail.com.au>
127962306a36Sopenharmony_ci		 */
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	return generic_set_freq(fe, c->frequency,
128362306a36Sopenharmony_ci				V4L2_TUNER_DIGITAL_TV, type, 0, demod);
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_cistatic int xc2028_sleep(struct dvb_frontend *fe)
128762306a36Sopenharmony_ci{
128862306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
128962306a36Sopenharmony_ci	int rc;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	rc = check_device_status(priv);
129262306a36Sopenharmony_ci	if (rc < 0)
129362306a36Sopenharmony_ci		return rc;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	/* Device is already in sleep mode */
129662306a36Sopenharmony_ci	if (!rc)
129762306a36Sopenharmony_ci		return 0;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	/* Avoid firmware reload on slow devices or if PM disabled */
130062306a36Sopenharmony_ci	if (no_poweroff || priv->ctrl.disable_power_mgmt)
130162306a36Sopenharmony_ci		return 0;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	tuner_dbg("Putting xc2028/3028 into poweroff mode.\n");
130462306a36Sopenharmony_ci	if (debug > 1) {
130562306a36Sopenharmony_ci		tuner_dbg("Printing sleep stack trace:\n");
130662306a36Sopenharmony_ci		dump_stack();
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	mutex_lock(&priv->lock);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	if (priv->firm_version < 0x0202)
131262306a36Sopenharmony_ci		rc = send_seq(priv, {0x00, XREG_POWER_DOWN, 0x00, 0x00});
131362306a36Sopenharmony_ci	else
131462306a36Sopenharmony_ci		rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00});
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	if (rc >= 0)
131762306a36Sopenharmony_ci		priv->state = XC2028_SLEEP;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	return rc;
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_cistatic void xc2028_dvb_release(struct dvb_frontend *fe)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	mutex_lock(&xc2028_list_mutex);
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	/* only perform final cleanup if this is the last instance */
133362306a36Sopenharmony_ci	if (hybrid_tuner_report_instance_count(priv) == 1)
133462306a36Sopenharmony_ci		free_firmware(priv);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	if (priv)
133762306a36Sopenharmony_ci		hybrid_tuner_release_state(priv);
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	mutex_unlock(&xc2028_list_mutex);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	fe->tuner_priv = NULL;
134262306a36Sopenharmony_ci}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_cistatic int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
134762306a36Sopenharmony_ci	int rc;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	rc = check_device_status(priv);
135262306a36Sopenharmony_ci	if (rc < 0)
135362306a36Sopenharmony_ci		return rc;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	*frequency = priv->frequency;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	return 0;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic void load_firmware_cb(const struct firmware *fw,
136162306a36Sopenharmony_ci			     void *context)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	struct dvb_frontend *fe = context;
136462306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
136562306a36Sopenharmony_ci	int rc;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error");
136862306a36Sopenharmony_ci	if (!fw) {
136962306a36Sopenharmony_ci		tuner_err("Could not load firmware %s.\n", priv->fname);
137062306a36Sopenharmony_ci		priv->state = XC2028_NODEV;
137162306a36Sopenharmony_ci		return;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	rc = load_all_firmwares(fe, fw);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	release_firmware(fw);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	if (rc < 0)
137962306a36Sopenharmony_ci		return;
138062306a36Sopenharmony_ci	priv->state = XC2028_ACTIVE;
138162306a36Sopenharmony_ci}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_cistatic int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
138462306a36Sopenharmony_ci{
138562306a36Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
138662306a36Sopenharmony_ci	struct xc2028_ctrl *p    = priv_cfg;
138762306a36Sopenharmony_ci	int                 rc   = 0;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	mutex_lock(&priv->lock);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	/*
139462306a36Sopenharmony_ci	 * Copy the config data.
139562306a36Sopenharmony_ci	 */
139662306a36Sopenharmony_ci	memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	/*
139962306a36Sopenharmony_ci	 * If firmware name changed, frees firmware. As free_firmware will
140062306a36Sopenharmony_ci	 * reset the status to NO_FIRMWARE, this forces a new request_firmware
140162306a36Sopenharmony_ci	 */
140262306a36Sopenharmony_ci	if (!firmware_name[0] && p->fname &&
140362306a36Sopenharmony_ci	    priv->fname && strcmp(p->fname, priv->fname))
140462306a36Sopenharmony_ci		free_firmware(priv);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	if (priv->ctrl.max_len < 9)
140762306a36Sopenharmony_ci		priv->ctrl.max_len = 13;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	if (priv->state == XC2028_NO_FIRMWARE) {
141062306a36Sopenharmony_ci		if (!firmware_name[0])
141162306a36Sopenharmony_ci			priv->fname = kstrdup(p->fname, GFP_KERNEL);
141262306a36Sopenharmony_ci		else
141362306a36Sopenharmony_ci			priv->fname = firmware_name;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci		if (!priv->fname) {
141662306a36Sopenharmony_ci			rc = -ENOMEM;
141762306a36Sopenharmony_ci			goto unlock;
141862306a36Sopenharmony_ci		}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci		rc = request_firmware_nowait(THIS_MODULE, 1,
142162306a36Sopenharmony_ci					     priv->fname,
142262306a36Sopenharmony_ci					     priv->i2c_props.adap->dev.parent,
142362306a36Sopenharmony_ci					     GFP_KERNEL,
142462306a36Sopenharmony_ci					     fe, load_firmware_cb);
142562306a36Sopenharmony_ci		if (rc < 0) {
142662306a36Sopenharmony_ci			tuner_err("Failed to request firmware %s\n",
142762306a36Sopenharmony_ci				  priv->fname);
142862306a36Sopenharmony_ci			priv->state = XC2028_NODEV;
142962306a36Sopenharmony_ci		} else
143062306a36Sopenharmony_ci			priv->state = XC2028_WAITING_FIRMWARE;
143162306a36Sopenharmony_ci	}
143262306a36Sopenharmony_ciunlock:
143362306a36Sopenharmony_ci	mutex_unlock(&priv->lock);
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	return rc;
143662306a36Sopenharmony_ci}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_cistatic const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
143962306a36Sopenharmony_ci	.info = {
144062306a36Sopenharmony_ci		 .name = "Xceive XC3028",
144162306a36Sopenharmony_ci		 .frequency_min_hz  =  42 * MHz,
144262306a36Sopenharmony_ci		 .frequency_max_hz  = 864 * MHz,
144362306a36Sopenharmony_ci		 .frequency_step_hz =  50 * kHz,
144462306a36Sopenharmony_ci		 },
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	.set_config	   = xc2028_set_config,
144762306a36Sopenharmony_ci	.set_analog_params = xc2028_set_analog_freq,
144862306a36Sopenharmony_ci	.release           = xc2028_dvb_release,
144962306a36Sopenharmony_ci	.get_frequency     = xc2028_get_frequency,
145062306a36Sopenharmony_ci	.get_rf_strength   = xc2028_signal,
145162306a36Sopenharmony_ci	.get_afc           = xc2028_get_afc,
145262306a36Sopenharmony_ci	.set_params        = xc2028_set_params,
145362306a36Sopenharmony_ci	.sleep             = xc2028_sleep,
145462306a36Sopenharmony_ci};
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_cistruct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
145762306a36Sopenharmony_ci				   struct xc2028_config *cfg)
145862306a36Sopenharmony_ci{
145962306a36Sopenharmony_ci	struct xc2028_data *priv;
146062306a36Sopenharmony_ci	int instance;
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	if (debug)
146362306a36Sopenharmony_ci		printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n");
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	if (NULL == cfg)
146662306a36Sopenharmony_ci		return NULL;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	if (!fe) {
146962306a36Sopenharmony_ci		printk(KERN_ERR "xc2028: No frontend!\n");
147062306a36Sopenharmony_ci		return NULL;
147162306a36Sopenharmony_ci	}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	mutex_lock(&xc2028_list_mutex);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	instance = hybrid_tuner_request_state(struct xc2028_data, priv,
147662306a36Sopenharmony_ci					      hybrid_tuner_instance_list,
147762306a36Sopenharmony_ci					      cfg->i2c_adap, cfg->i2c_addr,
147862306a36Sopenharmony_ci					      "xc2028");
147962306a36Sopenharmony_ci	switch (instance) {
148062306a36Sopenharmony_ci	case 0:
148162306a36Sopenharmony_ci		/* memory allocation failure */
148262306a36Sopenharmony_ci		goto fail;
148362306a36Sopenharmony_ci	case 1:
148462306a36Sopenharmony_ci		/* new tuner instance */
148562306a36Sopenharmony_ci		priv->ctrl.max_len = 13;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		mutex_init(&priv->lock);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci		fe->tuner_priv = priv;
149062306a36Sopenharmony_ci		break;
149162306a36Sopenharmony_ci	case 2:
149262306a36Sopenharmony_ci		/* existing tuner instance */
149362306a36Sopenharmony_ci		fe->tuner_priv = priv;
149462306a36Sopenharmony_ci		break;
149562306a36Sopenharmony_ci	}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
149862306a36Sopenharmony_ci	       sizeof(xc2028_dvb_tuner_ops));
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	if (cfg->ctrl)
150362306a36Sopenharmony_ci		xc2028_set_config(fe, cfg->ctrl);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	mutex_unlock(&xc2028_list_mutex);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	return fe;
150862306a36Sopenharmony_cifail:
150962306a36Sopenharmony_ci	mutex_unlock(&xc2028_list_mutex);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	xc2028_dvb_release(fe);
151262306a36Sopenharmony_ci	return NULL;
151362306a36Sopenharmony_ci}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xc2028_attach);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ciMODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
151862306a36Sopenharmony_ciMODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
151962306a36Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
152062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
152162306a36Sopenharmony_ciMODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
152262306a36Sopenharmony_ciMODULE_FIRMWARE(XC3028L_DEFAULT_FIRMWARE);
1523