18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// tuner-xc2028
38c2ecf20Sopenharmony_ci//
48c2ecf20Sopenharmony_ci// Copyright (c) 2007-2008 Mauro Carvalho Chehab <mchehab@kernel.org>
58c2ecf20Sopenharmony_ci//
68c2ecf20Sopenharmony_ci// Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
78c2ecf20Sopenharmony_ci//       - frontend interface
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/i2c.h>
108c2ecf20Sopenharmony_ci#include <asm/div64.h>
118c2ecf20Sopenharmony_ci#include <linux/firmware.h>
128c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <media/tuner.h>
158c2ecf20Sopenharmony_ci#include <linux/mutex.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
188c2ecf20Sopenharmony_ci#include "tuner-i2c.h"
198c2ecf20Sopenharmony_ci#include "tuner-xc2028.h"
208c2ecf20Sopenharmony_ci#include "tuner-xc2028-types.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <linux/dvb/frontend.h>
238c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Max transfer size done by I2C transfer functions */
268c2ecf20Sopenharmony_ci#define MAX_XFER_SIZE  80
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Registers (Write-only) */
298c2ecf20Sopenharmony_ci#define XREG_INIT         0x00
308c2ecf20Sopenharmony_ci#define XREG_RF_FREQ      0x02
318c2ecf20Sopenharmony_ci#define XREG_POWER_DOWN   0x08
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* Registers (Read-only) */
348c2ecf20Sopenharmony_ci#define XREG_FREQ_ERROR   0x01
358c2ecf20Sopenharmony_ci#define XREG_LOCK         0x02
368c2ecf20Sopenharmony_ci#define XREG_VERSION      0x04
378c2ecf20Sopenharmony_ci#define XREG_PRODUCT_ID   0x08
388c2ecf20Sopenharmony_ci#define XREG_HSYNC_FREQ   0x10
398c2ecf20Sopenharmony_ci#define XREG_FRAME_LINES  0x20
408c2ecf20Sopenharmony_ci#define XREG_SNR          0x40
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define XREG_ADC_ENV      0x0100
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int debug;
458c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "enable verbose debug messages");
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int no_poweroff;
498c2ecf20Sopenharmony_cimodule_param(no_poweroff, int, 0644);
508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n"
518c2ecf20Sopenharmony_ci	"1 keep device energized and with tuner ready all the times.\n"
528c2ecf20Sopenharmony_ci	"  Faster, but consumes more power and keeps the device hotter\n");
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic char audio_std[8];
558c2ecf20Sopenharmony_cimodule_param_string(audio_std, audio_std, sizeof(audio_std), 0);
568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(audio_std,
578c2ecf20Sopenharmony_ci	"Audio standard. XC3028 audio decoder explicitly needs to know what audio\n"
588c2ecf20Sopenharmony_ci	"standard is needed for some video standards with audio A2 or NICAM.\n"
598c2ecf20Sopenharmony_ci	"The valid values are:\n"
608c2ecf20Sopenharmony_ci	"A2\n"
618c2ecf20Sopenharmony_ci	"A2/A\n"
628c2ecf20Sopenharmony_ci	"A2/B\n"
638c2ecf20Sopenharmony_ci	"NICAM\n"
648c2ecf20Sopenharmony_ci	"NICAM/A\n"
658c2ecf20Sopenharmony_ci	"NICAM/B\n");
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic char firmware_name[30];
688c2ecf20Sopenharmony_cimodule_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(firmware_name,
708c2ecf20Sopenharmony_ci		 "Firmware file name. Allows overriding the default firmware name\n");
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic LIST_HEAD(hybrid_tuner_instance_list);
738c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(xc2028_list_mutex);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* struct for storing firmware table */
768c2ecf20Sopenharmony_cistruct firmware_description {
778c2ecf20Sopenharmony_ci	unsigned int  type;
788c2ecf20Sopenharmony_ci	v4l2_std_id   id;
798c2ecf20Sopenharmony_ci	__u16         int_freq;
808c2ecf20Sopenharmony_ci	unsigned char *ptr;
818c2ecf20Sopenharmony_ci	unsigned int  size;
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistruct firmware_properties {
858c2ecf20Sopenharmony_ci	unsigned int	type;
868c2ecf20Sopenharmony_ci	v4l2_std_id	id;
878c2ecf20Sopenharmony_ci	v4l2_std_id	std_req;
888c2ecf20Sopenharmony_ci	__u16		int_freq;
898c2ecf20Sopenharmony_ci	unsigned int	scode_table;
908c2ecf20Sopenharmony_ci	int		scode_nr;
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cienum xc2028_state {
948c2ecf20Sopenharmony_ci	XC2028_NO_FIRMWARE = 0,
958c2ecf20Sopenharmony_ci	XC2028_WAITING_FIRMWARE,
968c2ecf20Sopenharmony_ci	XC2028_ACTIVE,
978c2ecf20Sopenharmony_ci	XC2028_SLEEP,
988c2ecf20Sopenharmony_ci	XC2028_NODEV,
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistruct xc2028_data {
1028c2ecf20Sopenharmony_ci	struct list_head        hybrid_tuner_instance_list;
1038c2ecf20Sopenharmony_ci	struct tuner_i2c_props  i2c_props;
1048c2ecf20Sopenharmony_ci	__u32			frequency;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	enum xc2028_state	state;
1078c2ecf20Sopenharmony_ci	const char		*fname;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	struct firmware_description *firm;
1108c2ecf20Sopenharmony_ci	int			firm_size;
1118c2ecf20Sopenharmony_ci	__u16			firm_version;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	__u16			hwmodel;
1148c2ecf20Sopenharmony_ci	__u16			hwvers;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	struct xc2028_ctrl	ctrl;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	struct firmware_properties cur_fw;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	struct mutex lock;
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define i2c_send(priv, buf, size) ({					\
1248c2ecf20Sopenharmony_ci	int _rc;							\
1258c2ecf20Sopenharmony_ci	_rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size);		\
1268c2ecf20Sopenharmony_ci	if (size != _rc)						\
1278c2ecf20Sopenharmony_ci		tuner_info("i2c output error: rc = %d (should be %d)\n",\
1288c2ecf20Sopenharmony_ci			   _rc, (int)size);				\
1298c2ecf20Sopenharmony_ci	if (priv->ctrl.msleep)						\
1308c2ecf20Sopenharmony_ci		msleep(priv->ctrl.msleep);				\
1318c2ecf20Sopenharmony_ci	_rc;								\
1328c2ecf20Sopenharmony_ci})
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({		\
1358c2ecf20Sopenharmony_ci	int _rc;							\
1368c2ecf20Sopenharmony_ci	_rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize,	\
1378c2ecf20Sopenharmony_ci				       ibuf, isize);			\
1388c2ecf20Sopenharmony_ci	if (isize != _rc)						\
1398c2ecf20Sopenharmony_ci		tuner_err("i2c input error: rc = %d (should be %d)\n",	\
1408c2ecf20Sopenharmony_ci			   _rc, (int)isize);				\
1418c2ecf20Sopenharmony_ci	if (priv->ctrl.msleep)						\
1428c2ecf20Sopenharmony_ci		msleep(priv->ctrl.msleep);				\
1438c2ecf20Sopenharmony_ci	_rc;								\
1448c2ecf20Sopenharmony_ci})
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci#define send_seq(priv, data...)	({					\
1478c2ecf20Sopenharmony_ci	static u8 _val[] = data;					\
1488c2ecf20Sopenharmony_ci	int _rc;							\
1498c2ecf20Sopenharmony_ci	if (sizeof(_val) !=						\
1508c2ecf20Sopenharmony_ci			(_rc = tuner_i2c_xfer_send(&priv->i2c_props,	\
1518c2ecf20Sopenharmony_ci						_val, sizeof(_val)))) {	\
1528c2ecf20Sopenharmony_ci		tuner_err("Error on line %d: %d\n", __LINE__, _rc);	\
1538c2ecf20Sopenharmony_ci	} else if (priv->ctrl.msleep)					\
1548c2ecf20Sopenharmony_ci		msleep(priv->ctrl.msleep);				\
1558c2ecf20Sopenharmony_ci	_rc;								\
1568c2ecf20Sopenharmony_ci})
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	unsigned char buf[2];
1618c2ecf20Sopenharmony_ci	unsigned char ibuf[2];
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	tuner_dbg("%s %04x called\n", __func__, reg);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	buf[0] = reg >> 8;
1668c2ecf20Sopenharmony_ci	buf[1] = (unsigned char) reg;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)
1698c2ecf20Sopenharmony_ci		return -EIO;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	*val = (ibuf[1]) | (ibuf[0] << 8);
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci#define dump_firm_type(t)	dump_firm_type_and_int_freq(t, 0)
1768c2ecf20Sopenharmony_cistatic void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	if (type & BASE)
1798c2ecf20Sopenharmony_ci		printk(KERN_CONT "BASE ");
1808c2ecf20Sopenharmony_ci	if (type & INIT1)
1818c2ecf20Sopenharmony_ci		printk(KERN_CONT "INIT1 ");
1828c2ecf20Sopenharmony_ci	if (type & F8MHZ)
1838c2ecf20Sopenharmony_ci		printk(KERN_CONT "F8MHZ ");
1848c2ecf20Sopenharmony_ci	if (type & MTS)
1858c2ecf20Sopenharmony_ci		printk(KERN_CONT "MTS ");
1868c2ecf20Sopenharmony_ci	if (type & D2620)
1878c2ecf20Sopenharmony_ci		printk(KERN_CONT "D2620 ");
1888c2ecf20Sopenharmony_ci	if (type & D2633)
1898c2ecf20Sopenharmony_ci		printk(KERN_CONT "D2633 ");
1908c2ecf20Sopenharmony_ci	if (type & DTV6)
1918c2ecf20Sopenharmony_ci		printk(KERN_CONT "DTV6 ");
1928c2ecf20Sopenharmony_ci	if (type & QAM)
1938c2ecf20Sopenharmony_ci		printk(KERN_CONT "QAM ");
1948c2ecf20Sopenharmony_ci	if (type & DTV7)
1958c2ecf20Sopenharmony_ci		printk(KERN_CONT "DTV7 ");
1968c2ecf20Sopenharmony_ci	if (type & DTV78)
1978c2ecf20Sopenharmony_ci		printk(KERN_CONT "DTV78 ");
1988c2ecf20Sopenharmony_ci	if (type & DTV8)
1998c2ecf20Sopenharmony_ci		printk(KERN_CONT "DTV8 ");
2008c2ecf20Sopenharmony_ci	if (type & FM)
2018c2ecf20Sopenharmony_ci		printk(KERN_CONT "FM ");
2028c2ecf20Sopenharmony_ci	if (type & INPUT1)
2038c2ecf20Sopenharmony_ci		printk(KERN_CONT "INPUT1 ");
2048c2ecf20Sopenharmony_ci	if (type & LCD)
2058c2ecf20Sopenharmony_ci		printk(KERN_CONT "LCD ");
2068c2ecf20Sopenharmony_ci	if (type & NOGD)
2078c2ecf20Sopenharmony_ci		printk(KERN_CONT "NOGD ");
2088c2ecf20Sopenharmony_ci	if (type & MONO)
2098c2ecf20Sopenharmony_ci		printk(KERN_CONT "MONO ");
2108c2ecf20Sopenharmony_ci	if (type & ATSC)
2118c2ecf20Sopenharmony_ci		printk(KERN_CONT "ATSC ");
2128c2ecf20Sopenharmony_ci	if (type & IF)
2138c2ecf20Sopenharmony_ci		printk(KERN_CONT "IF ");
2148c2ecf20Sopenharmony_ci	if (type & LG60)
2158c2ecf20Sopenharmony_ci		printk(KERN_CONT "LG60 ");
2168c2ecf20Sopenharmony_ci	if (type & ATI638)
2178c2ecf20Sopenharmony_ci		printk(KERN_CONT "ATI638 ");
2188c2ecf20Sopenharmony_ci	if (type & OREN538)
2198c2ecf20Sopenharmony_ci		printk(KERN_CONT "OREN538 ");
2208c2ecf20Sopenharmony_ci	if (type & OREN36)
2218c2ecf20Sopenharmony_ci		printk(KERN_CONT "OREN36 ");
2228c2ecf20Sopenharmony_ci	if (type & TOYOTA388)
2238c2ecf20Sopenharmony_ci		printk(KERN_CONT "TOYOTA388 ");
2248c2ecf20Sopenharmony_ci	if (type & TOYOTA794)
2258c2ecf20Sopenharmony_ci		printk(KERN_CONT "TOYOTA794 ");
2268c2ecf20Sopenharmony_ci	if (type & DIBCOM52)
2278c2ecf20Sopenharmony_ci		printk(KERN_CONT "DIBCOM52 ");
2288c2ecf20Sopenharmony_ci	if (type & ZARLINK456)
2298c2ecf20Sopenharmony_ci		printk(KERN_CONT "ZARLINK456 ");
2308c2ecf20Sopenharmony_ci	if (type & CHINA)
2318c2ecf20Sopenharmony_ci		printk(KERN_CONT "CHINA ");
2328c2ecf20Sopenharmony_ci	if (type & F6MHZ)
2338c2ecf20Sopenharmony_ci		printk(KERN_CONT "F6MHZ ");
2348c2ecf20Sopenharmony_ci	if (type & INPUT2)
2358c2ecf20Sopenharmony_ci		printk(KERN_CONT "INPUT2 ");
2368c2ecf20Sopenharmony_ci	if (type & SCODE)
2378c2ecf20Sopenharmony_ci		printk(KERN_CONT "SCODE ");
2388c2ecf20Sopenharmony_ci	if (type & HAS_IF)
2398c2ecf20Sopenharmony_ci		printk(KERN_CONT "HAS_IF_%d ", int_freq);
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic  v4l2_std_id parse_audio_std_option(void)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	if (strcasecmp(audio_std, "A2") == 0)
2458c2ecf20Sopenharmony_ci		return V4L2_STD_A2;
2468c2ecf20Sopenharmony_ci	if (strcasecmp(audio_std, "A2/A") == 0)
2478c2ecf20Sopenharmony_ci		return V4L2_STD_A2_A;
2488c2ecf20Sopenharmony_ci	if (strcasecmp(audio_std, "A2/B") == 0)
2498c2ecf20Sopenharmony_ci		return V4L2_STD_A2_B;
2508c2ecf20Sopenharmony_ci	if (strcasecmp(audio_std, "NICAM") == 0)
2518c2ecf20Sopenharmony_ci		return V4L2_STD_NICAM;
2528c2ecf20Sopenharmony_ci	if (strcasecmp(audio_std, "NICAM/A") == 0)
2538c2ecf20Sopenharmony_ci		return V4L2_STD_NICAM_A;
2548c2ecf20Sopenharmony_ci	if (strcasecmp(audio_std, "NICAM/B") == 0)
2558c2ecf20Sopenharmony_ci		return V4L2_STD_NICAM_B;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int check_device_status(struct xc2028_data *priv)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	switch (priv->state) {
2638c2ecf20Sopenharmony_ci	case XC2028_NO_FIRMWARE:
2648c2ecf20Sopenharmony_ci	case XC2028_WAITING_FIRMWARE:
2658c2ecf20Sopenharmony_ci		return -EAGAIN;
2668c2ecf20Sopenharmony_ci	case XC2028_ACTIVE:
2678c2ecf20Sopenharmony_ci		return 1;
2688c2ecf20Sopenharmony_ci	case XC2028_SLEEP:
2698c2ecf20Sopenharmony_ci		return 0;
2708c2ecf20Sopenharmony_ci	case XC2028_NODEV:
2718c2ecf20Sopenharmony_ci		return -ENODEV;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci	return 0;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic void free_firmware(struct xc2028_data *priv)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	int i;
2798c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	/* free allocated f/w string */
2828c2ecf20Sopenharmony_ci	if (priv->fname != firmware_name)
2838c2ecf20Sopenharmony_ci		kfree(priv->fname);
2848c2ecf20Sopenharmony_ci	priv->fname = NULL;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	priv->state = XC2028_NO_FIRMWARE;
2878c2ecf20Sopenharmony_ci	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (!priv->firm)
2908c2ecf20Sopenharmony_ci		return;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	for (i = 0; i < priv->firm_size; i++)
2938c2ecf20Sopenharmony_ci		kfree(priv->firm[i].ptr);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	kfree(priv->firm);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	priv->firm = NULL;
2988c2ecf20Sopenharmony_ci	priv->firm_size = 0;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int load_all_firmwares(struct dvb_frontend *fe,
3028c2ecf20Sopenharmony_ci			      const struct firmware *fw)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct xc2028_data    *priv = fe->tuner_priv;
3058c2ecf20Sopenharmony_ci	const unsigned char   *p, *endp;
3068c2ecf20Sopenharmony_ci	int                   rc = 0;
3078c2ecf20Sopenharmony_ci	int		      n, n_array;
3088c2ecf20Sopenharmony_ci	char		      name[33];
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	p = fw->data;
3138c2ecf20Sopenharmony_ci	endp = p + fw->size;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (fw->size < sizeof(name) - 1 + 2 + 2) {
3168c2ecf20Sopenharmony_ci		tuner_err("Error: firmware file %s has invalid size!\n",
3178c2ecf20Sopenharmony_ci			  priv->fname);
3188c2ecf20Sopenharmony_ci		goto corrupt;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	memcpy(name, p, sizeof(name) - 1);
3228c2ecf20Sopenharmony_ci	name[sizeof(name) - 1] = 0;
3238c2ecf20Sopenharmony_ci	p += sizeof(name) - 1;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	priv->firm_version = get_unaligned_le16(p);
3268c2ecf20Sopenharmony_ci	p += 2;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	n_array = get_unaligned_le16(p);
3298c2ecf20Sopenharmony_ci	p += 2;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
3328c2ecf20Sopenharmony_ci		   n_array, priv->fname, name,
3338c2ecf20Sopenharmony_ci		   priv->firm_version >> 8, priv->firm_version & 0xff);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL);
3368c2ecf20Sopenharmony_ci	if (priv->firm == NULL) {
3378c2ecf20Sopenharmony_ci		tuner_err("Not enough memory to load firmware file.\n");
3388c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3398c2ecf20Sopenharmony_ci		goto err;
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci	priv->firm_size = n_array;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	n = -1;
3448c2ecf20Sopenharmony_ci	while (p < endp) {
3458c2ecf20Sopenharmony_ci		__u32 type, size;
3468c2ecf20Sopenharmony_ci		v4l2_std_id id;
3478c2ecf20Sopenharmony_ci		__u16 int_freq = 0;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci		n++;
3508c2ecf20Sopenharmony_ci		if (n >= n_array) {
3518c2ecf20Sopenharmony_ci			tuner_err("More firmware images in file than were expected!\n");
3528c2ecf20Sopenharmony_ci			goto corrupt;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		/* Checks if there's enough bytes to read */
3568c2ecf20Sopenharmony_ci		if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))
3578c2ecf20Sopenharmony_ci			goto header;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		type = get_unaligned_le32(p);
3608c2ecf20Sopenharmony_ci		p += sizeof(type);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		id = get_unaligned_le64(p);
3638c2ecf20Sopenharmony_ci		p += sizeof(id);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		if (type & HAS_IF) {
3668c2ecf20Sopenharmony_ci			int_freq = get_unaligned_le16(p);
3678c2ecf20Sopenharmony_ci			p += sizeof(int_freq);
3688c2ecf20Sopenharmony_ci			if (endp - p < sizeof(size))
3698c2ecf20Sopenharmony_ci				goto header;
3708c2ecf20Sopenharmony_ci		}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		size = get_unaligned_le32(p);
3738c2ecf20Sopenharmony_ci		p += sizeof(size);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		if (!size || size > endp - p) {
3768c2ecf20Sopenharmony_ci			tuner_err("Firmware type ");
3778c2ecf20Sopenharmony_ci			dump_firm_type(type);
3788c2ecf20Sopenharmony_ci			printk(KERN_CONT
3798c2ecf20Sopenharmony_ci			       "(%x), id %llx is corrupted (size=%zd, expected %d)\n",
3808c2ecf20Sopenharmony_ci			       type, (unsigned long long)id, (endp - p), size);
3818c2ecf20Sopenharmony_ci			goto corrupt;
3828c2ecf20Sopenharmony_ci		}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		priv->firm[n].ptr = kmemdup(p, size, GFP_KERNEL);
3858c2ecf20Sopenharmony_ci		if (priv->firm[n].ptr == NULL) {
3868c2ecf20Sopenharmony_ci			tuner_err("Not enough memory to load firmware file.\n");
3878c2ecf20Sopenharmony_ci			rc = -ENOMEM;
3888c2ecf20Sopenharmony_ci			goto err;
3898c2ecf20Sopenharmony_ci		}
3908c2ecf20Sopenharmony_ci		tuner_dbg("Reading firmware type ");
3918c2ecf20Sopenharmony_ci		if (debug) {
3928c2ecf20Sopenharmony_ci			dump_firm_type_and_int_freq(type, int_freq);
3938c2ecf20Sopenharmony_ci			printk(KERN_CONT "(%x), id %llx, size=%d.\n",
3948c2ecf20Sopenharmony_ci			       type, (unsigned long long)id, size);
3958c2ecf20Sopenharmony_ci		}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		priv->firm[n].type = type;
3988c2ecf20Sopenharmony_ci		priv->firm[n].id   = id;
3998c2ecf20Sopenharmony_ci		priv->firm[n].size = size;
4008c2ecf20Sopenharmony_ci		priv->firm[n].int_freq = int_freq;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		p += size;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (n + 1 != priv->firm_size) {
4068c2ecf20Sopenharmony_ci		tuner_err("Firmware file is incomplete!\n");
4078c2ecf20Sopenharmony_ci		goto corrupt;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	goto done;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ciheader:
4138c2ecf20Sopenharmony_ci	tuner_err("Firmware header is incomplete!\n");
4148c2ecf20Sopenharmony_cicorrupt:
4158c2ecf20Sopenharmony_ci	rc = -EINVAL;
4168c2ecf20Sopenharmony_ci	tuner_err("Error: firmware file is corrupted!\n");
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cierr:
4198c2ecf20Sopenharmony_ci	tuner_info("Releasing partially loaded firmware file.\n");
4208c2ecf20Sopenharmony_ci	free_firmware(priv);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cidone:
4238c2ecf20Sopenharmony_ci	if (rc == 0)
4248c2ecf20Sopenharmony_ci		tuner_dbg("Firmware files loaded.\n");
4258c2ecf20Sopenharmony_ci	else
4268c2ecf20Sopenharmony_ci		priv->state = XC2028_NODEV;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return rc;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic int seek_firmware(struct dvb_frontend *fe, unsigned int type,
4328c2ecf20Sopenharmony_ci			 v4l2_std_id *id)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
4358c2ecf20Sopenharmony_ci	int                 i, best_i = -1, best_nr_matches = 0;
4368c2ecf20Sopenharmony_ci	unsigned int        type_mask = 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	tuner_dbg("%s called, want type=", __func__);
4398c2ecf20Sopenharmony_ci	if (debug) {
4408c2ecf20Sopenharmony_ci		dump_firm_type(type);
4418c2ecf20Sopenharmony_ci		printk(KERN_CONT "(%x), id %016llx.\n",
4428c2ecf20Sopenharmony_ci		       type, (unsigned long long)*id);
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (!priv->firm) {
4468c2ecf20Sopenharmony_ci		tuner_err("Error! firmware not loaded\n");
4478c2ecf20Sopenharmony_ci		return -EINVAL;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (((type & ~SCODE) == 0) && (*id == 0))
4518c2ecf20Sopenharmony_ci		*id = V4L2_STD_PAL;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	if (type & BASE)
4548c2ecf20Sopenharmony_ci		type_mask = BASE_TYPES;
4558c2ecf20Sopenharmony_ci	else if (type & SCODE) {
4568c2ecf20Sopenharmony_ci		type &= SCODE_TYPES;
4578c2ecf20Sopenharmony_ci		type_mask = SCODE_TYPES & ~HAS_IF;
4588c2ecf20Sopenharmony_ci	} else if (type & DTV_TYPES)
4598c2ecf20Sopenharmony_ci		type_mask = DTV_TYPES;
4608c2ecf20Sopenharmony_ci	else if (type & STD_SPECIFIC_TYPES)
4618c2ecf20Sopenharmony_ci		type_mask = STD_SPECIFIC_TYPES;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	type &= type_mask;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (!(type & SCODE))
4668c2ecf20Sopenharmony_ci		type_mask = ~0;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* Seek for exact match */
4698c2ecf20Sopenharmony_ci	for (i = 0; i < priv->firm_size; i++) {
4708c2ecf20Sopenharmony_ci		if ((type == (priv->firm[i].type & type_mask)) &&
4718c2ecf20Sopenharmony_ci		    (*id == priv->firm[i].id))
4728c2ecf20Sopenharmony_ci			goto found;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	/* Seek for generic video standard match */
4768c2ecf20Sopenharmony_ci	for (i = 0; i < priv->firm_size; i++) {
4778c2ecf20Sopenharmony_ci		v4l2_std_id match_mask;
4788c2ecf20Sopenharmony_ci		int nr_matches;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		if (type != (priv->firm[i].type & type_mask))
4818c2ecf20Sopenharmony_ci			continue;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		match_mask = *id & priv->firm[i].id;
4848c2ecf20Sopenharmony_ci		if (!match_mask)
4858c2ecf20Sopenharmony_ci			continue;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		if ((*id & match_mask) == *id)
4888c2ecf20Sopenharmony_ci			goto found; /* Supports all the requested standards */
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		nr_matches = hweight64(match_mask);
4918c2ecf20Sopenharmony_ci		if (nr_matches > best_nr_matches) {
4928c2ecf20Sopenharmony_ci			best_nr_matches = nr_matches;
4938c2ecf20Sopenharmony_ci			best_i = i;
4948c2ecf20Sopenharmony_ci		}
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (best_nr_matches > 0) {
4988c2ecf20Sopenharmony_ci		tuner_dbg("Selecting best matching firmware (%d bits) for type=",
4998c2ecf20Sopenharmony_ci			  best_nr_matches);
5008c2ecf20Sopenharmony_ci		dump_firm_type(type);
5018c2ecf20Sopenharmony_ci		printk(KERN_CONT
5028c2ecf20Sopenharmony_ci		       "(%x), id %016llx:\n", type, (unsigned long long)*id);
5038c2ecf20Sopenharmony_ci		i = best_i;
5048c2ecf20Sopenharmony_ci		goto found;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	/*FIXME: Would make sense to seek for type "hint" match ? */
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	i = -ENOENT;
5108c2ecf20Sopenharmony_ci	goto ret;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cifound:
5138c2ecf20Sopenharmony_ci	*id = priv->firm[i].id;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ciret:
5168c2ecf20Sopenharmony_ci	tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found");
5178c2ecf20Sopenharmony_ci	if (debug) {
5188c2ecf20Sopenharmony_ci		dump_firm_type(type);
5198c2ecf20Sopenharmony_ci		printk(KERN_CONT "(%x), id %016llx.\n",
5208c2ecf20Sopenharmony_ci		       type, (unsigned long long)*id);
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci	return i;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* analog side (tuner-core) uses i2c_adap->algo_data.
5308c2ecf20Sopenharmony_ci	 * digital side is not guaranteed to have algo_data defined.
5318c2ecf20Sopenharmony_ci	 *
5328c2ecf20Sopenharmony_ci	 * digital side will always have fe->dvb defined.
5338c2ecf20Sopenharmony_ci	 * analog side (tuner-core) doesn't (yet) define fe->dvb.
5348c2ecf20Sopenharmony_ci	 */
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	return (!fe->callback) ? -EINVAL :
5378c2ecf20Sopenharmony_ci		fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
5388c2ecf20Sopenharmony_ci				fe->dvb->priv : priv->i2c_props.adap->algo_data,
5398c2ecf20Sopenharmony_ci			     DVB_FRONTEND_COMPONENT_TUNER, cmd, arg);
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_cistatic int load_firmware(struct dvb_frontend *fe, unsigned int type,
5438c2ecf20Sopenharmony_ci			 v4l2_std_id *id)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
5468c2ecf20Sopenharmony_ci	int                pos, rc;
5478c2ecf20Sopenharmony_ci	unsigned char      *p, *endp, buf[MAX_XFER_SIZE];
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (priv->ctrl.max_len > sizeof(buf))
5508c2ecf20Sopenharmony_ci		priv->ctrl.max_len = sizeof(buf);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	pos = seek_firmware(fe, type, id);
5558c2ecf20Sopenharmony_ci	if (pos < 0)
5568c2ecf20Sopenharmony_ci		return pos;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	tuner_info("Loading firmware for type=");
5598c2ecf20Sopenharmony_ci	dump_firm_type(priv->firm[pos].type);
5608c2ecf20Sopenharmony_ci	printk(KERN_CONT "(%x), id %016llx.\n",
5618c2ecf20Sopenharmony_ci	       priv->firm[pos].type, (unsigned long long)*id);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	p = priv->firm[pos].ptr;
5648c2ecf20Sopenharmony_ci	endp = p + priv->firm[pos].size;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	while (p < endp) {
5678c2ecf20Sopenharmony_ci		__u16 size;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		/* Checks if there's enough bytes to read */
5708c2ecf20Sopenharmony_ci		if (p + sizeof(size) > endp) {
5718c2ecf20Sopenharmony_ci			tuner_err("Firmware chunk size is wrong\n");
5728c2ecf20Sopenharmony_ci			return -EINVAL;
5738c2ecf20Sopenharmony_ci		}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		size = le16_to_cpu(*(__le16 *) p);
5768c2ecf20Sopenharmony_ci		p += sizeof(size);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci		if (size == 0xffff)
5798c2ecf20Sopenharmony_ci			return 0;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci		if (!size) {
5828c2ecf20Sopenharmony_ci			/* Special callback command received */
5838c2ecf20Sopenharmony_ci			rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
5848c2ecf20Sopenharmony_ci			if (rc < 0) {
5858c2ecf20Sopenharmony_ci				tuner_err("Error at RESET code %d\n",
5868c2ecf20Sopenharmony_ci					   (*p) & 0x7f);
5878c2ecf20Sopenharmony_ci				return -EINVAL;
5888c2ecf20Sopenharmony_ci			}
5898c2ecf20Sopenharmony_ci			continue;
5908c2ecf20Sopenharmony_ci		}
5918c2ecf20Sopenharmony_ci		if (size >= 0xff00) {
5928c2ecf20Sopenharmony_ci			switch (size) {
5938c2ecf20Sopenharmony_ci			case 0xff00:
5948c2ecf20Sopenharmony_ci				rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0);
5958c2ecf20Sopenharmony_ci				if (rc < 0) {
5968c2ecf20Sopenharmony_ci					tuner_err("Error at RESET code %d\n",
5978c2ecf20Sopenharmony_ci						  (*p) & 0x7f);
5988c2ecf20Sopenharmony_ci					return -EINVAL;
5998c2ecf20Sopenharmony_ci				}
6008c2ecf20Sopenharmony_ci				break;
6018c2ecf20Sopenharmony_ci			default:
6028c2ecf20Sopenharmony_ci				tuner_info("Invalid RESET code %d\n",
6038c2ecf20Sopenharmony_ci					   size & 0x7f);
6048c2ecf20Sopenharmony_ci				return -EINVAL;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci			}
6078c2ecf20Sopenharmony_ci			continue;
6088c2ecf20Sopenharmony_ci		}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci		/* Checks for a sleep command */
6118c2ecf20Sopenharmony_ci		if (size & 0x8000) {
6128c2ecf20Sopenharmony_ci			msleep(size & 0x7fff);
6138c2ecf20Sopenharmony_ci			continue;
6148c2ecf20Sopenharmony_ci		}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci		if ((size + p > endp)) {
6178c2ecf20Sopenharmony_ci			tuner_err("missing bytes: need %d, have %zd\n",
6188c2ecf20Sopenharmony_ci				   size, (endp - p));
6198c2ecf20Sopenharmony_ci			return -EINVAL;
6208c2ecf20Sopenharmony_ci		}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		buf[0] = *p;
6238c2ecf20Sopenharmony_ci		p++;
6248c2ecf20Sopenharmony_ci		size--;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci		/* Sends message chunks */
6278c2ecf20Sopenharmony_ci		while (size > 0) {
6288c2ecf20Sopenharmony_ci			int len = (size < priv->ctrl.max_len - 1) ?
6298c2ecf20Sopenharmony_ci				   size : priv->ctrl.max_len - 1;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci			memcpy(buf + 1, p, len);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci			rc = i2c_send(priv, buf, len + 1);
6348c2ecf20Sopenharmony_ci			if (rc < 0) {
6358c2ecf20Sopenharmony_ci				tuner_err("%d returned from send\n", rc);
6368c2ecf20Sopenharmony_ci				return -EINVAL;
6378c2ecf20Sopenharmony_ci			}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci			p += len;
6408c2ecf20Sopenharmony_ci			size -= len;
6418c2ecf20Sopenharmony_ci		}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci		/* silently fail if the frontend doesn't support I2C flush */
6448c2ecf20Sopenharmony_ci		rc = do_tuner_callback(fe, XC2028_I2C_FLUSH, 0);
6458c2ecf20Sopenharmony_ci		if ((rc < 0) && (rc != -EINVAL)) {
6468c2ecf20Sopenharmony_ci			tuner_err("error executing flush: %d\n", rc);
6478c2ecf20Sopenharmony_ci			return rc;
6488c2ecf20Sopenharmony_ci		}
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci	return 0;
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic int load_scode(struct dvb_frontend *fe, unsigned int type,
6548c2ecf20Sopenharmony_ci			 v4l2_std_id *id, __u16 int_freq, int scode)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
6578c2ecf20Sopenharmony_ci	int                pos, rc;
6588c2ecf20Sopenharmony_ci	unsigned char	   *p;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	if (!int_freq) {
6638c2ecf20Sopenharmony_ci		pos = seek_firmware(fe, type, id);
6648c2ecf20Sopenharmony_ci		if (pos < 0)
6658c2ecf20Sopenharmony_ci			return pos;
6668c2ecf20Sopenharmony_ci	} else {
6678c2ecf20Sopenharmony_ci		for (pos = 0; pos < priv->firm_size; pos++) {
6688c2ecf20Sopenharmony_ci			if ((priv->firm[pos].int_freq == int_freq) &&
6698c2ecf20Sopenharmony_ci			    (priv->firm[pos].type & HAS_IF))
6708c2ecf20Sopenharmony_ci				break;
6718c2ecf20Sopenharmony_ci		}
6728c2ecf20Sopenharmony_ci		if (pos == priv->firm_size)
6738c2ecf20Sopenharmony_ci			return -ENOENT;
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	p = priv->firm[pos].ptr;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if (priv->firm[pos].type & HAS_IF) {
6798c2ecf20Sopenharmony_ci		if (priv->firm[pos].size != 12 * 16 || scode >= 16)
6808c2ecf20Sopenharmony_ci			return -EINVAL;
6818c2ecf20Sopenharmony_ci		p += 12 * scode;
6828c2ecf20Sopenharmony_ci	} else {
6838c2ecf20Sopenharmony_ci		/* 16 SCODE entries per file; each SCODE entry is 12 bytes and
6848c2ecf20Sopenharmony_ci		 * has a 2-byte size header in the firmware format. */
6858c2ecf20Sopenharmony_ci		if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||
6868c2ecf20Sopenharmony_ci		    le16_to_cpu(*(__le16 *)(p + 14 * scode)) != 12)
6878c2ecf20Sopenharmony_ci			return -EINVAL;
6888c2ecf20Sopenharmony_ci		p += 14 * scode + 2;
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	tuner_info("Loading SCODE for type=");
6928c2ecf20Sopenharmony_ci	dump_firm_type_and_int_freq(priv->firm[pos].type,
6938c2ecf20Sopenharmony_ci				    priv->firm[pos].int_freq);
6948c2ecf20Sopenharmony_ci	printk(KERN_CONT "(%x), id %016llx.\n", priv->firm[pos].type,
6958c2ecf20Sopenharmony_ci	       (unsigned long long)*id);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	if (priv->firm_version < 0x0202)
6988c2ecf20Sopenharmony_ci		rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00});
6998c2ecf20Sopenharmony_ci	else
7008c2ecf20Sopenharmony_ci		rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00});
7018c2ecf20Sopenharmony_ci	if (rc < 0)
7028c2ecf20Sopenharmony_ci		return -EIO;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	rc = i2c_send(priv, p, 12);
7058c2ecf20Sopenharmony_ci	if (rc < 0)
7068c2ecf20Sopenharmony_ci		return -EIO;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	rc = send_seq(priv, {0x00, 0x8c});
7098c2ecf20Sopenharmony_ci	if (rc < 0)
7108c2ecf20Sopenharmony_ci		return -EIO;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	return 0;
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic int xc2028_sleep(struct dvb_frontend *fe);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_cistatic int check_firmware(struct dvb_frontend *fe, unsigned int type,
7188c2ecf20Sopenharmony_ci			  v4l2_std_id std, __u16 int_freq)
7198c2ecf20Sopenharmony_ci{
7208c2ecf20Sopenharmony_ci	struct xc2028_data         *priv = fe->tuner_priv;
7218c2ecf20Sopenharmony_ci	struct firmware_properties new_fw;
7228c2ecf20Sopenharmony_ci	int			   rc, retry_count = 0;
7238c2ecf20Sopenharmony_ci	u16			   version, hwmodel;
7248c2ecf20Sopenharmony_ci	v4l2_std_id		   std0;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	rc = check_device_status(priv);
7298c2ecf20Sopenharmony_ci	if (rc < 0)
7308c2ecf20Sopenharmony_ci		return rc;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	if (priv->ctrl.mts && !(type & FM))
7338c2ecf20Sopenharmony_ci		type |= MTS;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ciretry:
7368c2ecf20Sopenharmony_ci	new_fw.type = type;
7378c2ecf20Sopenharmony_ci	new_fw.id = std;
7388c2ecf20Sopenharmony_ci	new_fw.std_req = std;
7398c2ecf20Sopenharmony_ci	new_fw.scode_table = SCODE | priv->ctrl.scode_table;
7408c2ecf20Sopenharmony_ci	new_fw.scode_nr = 0;
7418c2ecf20Sopenharmony_ci	new_fw.int_freq = int_freq;
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	tuner_dbg("checking firmware, user requested type=");
7448c2ecf20Sopenharmony_ci	if (debug) {
7458c2ecf20Sopenharmony_ci		dump_firm_type(new_fw.type);
7468c2ecf20Sopenharmony_ci		printk(KERN_CONT "(%x), id %016llx, ", new_fw.type,
7478c2ecf20Sopenharmony_ci		       (unsigned long long)new_fw.std_req);
7488c2ecf20Sopenharmony_ci		if (!int_freq) {
7498c2ecf20Sopenharmony_ci			printk(KERN_CONT "scode_tbl ");
7508c2ecf20Sopenharmony_ci			dump_firm_type(priv->ctrl.scode_table);
7518c2ecf20Sopenharmony_ci			printk(KERN_CONT "(%x), ", priv->ctrl.scode_table);
7528c2ecf20Sopenharmony_ci		} else
7538c2ecf20Sopenharmony_ci			printk(KERN_CONT "int_freq %d, ", new_fw.int_freq);
7548c2ecf20Sopenharmony_ci		printk(KERN_CONT "scode_nr %d\n", new_fw.scode_nr);
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/*
7588c2ecf20Sopenharmony_ci	 * No need to reload base firmware if it matches and if the tuner
7598c2ecf20Sopenharmony_ci	 * is not at sleep mode
7608c2ecf20Sopenharmony_ci	 */
7618c2ecf20Sopenharmony_ci	if ((priv->state == XC2028_ACTIVE) &&
7628c2ecf20Sopenharmony_ci	    (((BASE | new_fw.type) & BASE_TYPES) ==
7638c2ecf20Sopenharmony_ci	    (priv->cur_fw.type & BASE_TYPES))) {
7648c2ecf20Sopenharmony_ci		tuner_dbg("BASE firmware not changed.\n");
7658c2ecf20Sopenharmony_ci		goto skip_base;
7668c2ecf20Sopenharmony_ci	}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/* Updating BASE - forget about all currently loaded firmware */
7698c2ecf20Sopenharmony_ci	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	/* Reset is needed before loading firmware */
7728c2ecf20Sopenharmony_ci	rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
7738c2ecf20Sopenharmony_ci	if (rc < 0)
7748c2ecf20Sopenharmony_ci		goto fail;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* BASE firmwares are all std0 */
7778c2ecf20Sopenharmony_ci	std0 = 0;
7788c2ecf20Sopenharmony_ci	rc = load_firmware(fe, BASE | new_fw.type, &std0);
7798c2ecf20Sopenharmony_ci	if (rc < 0) {
7808c2ecf20Sopenharmony_ci		tuner_err("Error %d while loading base firmware\n",
7818c2ecf20Sopenharmony_ci			  rc);
7828c2ecf20Sopenharmony_ci		goto fail;
7838c2ecf20Sopenharmony_ci	}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	/* Load INIT1, if needed */
7868c2ecf20Sopenharmony_ci	tuner_dbg("Load init1 firmware, if exists\n");
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0);
7898c2ecf20Sopenharmony_ci	if (rc == -ENOENT)
7908c2ecf20Sopenharmony_ci		rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ,
7918c2ecf20Sopenharmony_ci				   &std0);
7928c2ecf20Sopenharmony_ci	if (rc < 0 && rc != -ENOENT) {
7938c2ecf20Sopenharmony_ci		tuner_err("Error %d while loading init1 firmware\n",
7948c2ecf20Sopenharmony_ci			  rc);
7958c2ecf20Sopenharmony_ci		goto fail;
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ciskip_base:
7998c2ecf20Sopenharmony_ci	/*
8008c2ecf20Sopenharmony_ci	 * No need to reload standard specific firmware if base firmware
8018c2ecf20Sopenharmony_ci	 * was not reloaded and requested video standards have not changed.
8028c2ecf20Sopenharmony_ci	 */
8038c2ecf20Sopenharmony_ci	if (priv->cur_fw.type == (BASE | new_fw.type) &&
8048c2ecf20Sopenharmony_ci	    priv->cur_fw.std_req == std) {
8058c2ecf20Sopenharmony_ci		tuner_dbg("Std-specific firmware already loaded.\n");
8068c2ecf20Sopenharmony_ci		goto skip_std_specific;
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	/* Reloading std-specific firmware forces a SCODE update */
8108c2ecf20Sopenharmony_ci	priv->cur_fw.scode_table = 0;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	rc = load_firmware(fe, new_fw.type, &new_fw.id);
8138c2ecf20Sopenharmony_ci	if (rc == -ENOENT)
8148c2ecf20Sopenharmony_ci		rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (rc < 0)
8178c2ecf20Sopenharmony_ci		goto fail;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ciskip_std_specific:
8208c2ecf20Sopenharmony_ci	if (priv->cur_fw.scode_table == new_fw.scode_table &&
8218c2ecf20Sopenharmony_ci	    priv->cur_fw.scode_nr == new_fw.scode_nr) {
8228c2ecf20Sopenharmony_ci		tuner_dbg("SCODE firmware already loaded.\n");
8238c2ecf20Sopenharmony_ci		goto check_device;
8248c2ecf20Sopenharmony_ci	}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (new_fw.type & FM)
8278c2ecf20Sopenharmony_ci		goto check_device;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/* Load SCODE firmware, if exists */
8308c2ecf20Sopenharmony_ci	tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
8338c2ecf20Sopenharmony_ci			new_fw.int_freq, new_fw.scode_nr);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cicheck_device:
8368c2ecf20Sopenharmony_ci	if (xc2028_get_reg(priv, 0x0004, &version) < 0 ||
8378c2ecf20Sopenharmony_ci	    xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) {
8388c2ecf20Sopenharmony_ci		tuner_err("Unable to read tuner registers.\n");
8398c2ecf20Sopenharmony_ci		goto fail;
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	tuner_dbg("Device is Xceive %d version %d.%d, firmware version %d.%d\n",
8438c2ecf20Sopenharmony_ci		  hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
8448c2ecf20Sopenharmony_ci		  (version & 0xf0) >> 4, version & 0xf);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (priv->ctrl.read_not_reliable)
8488c2ecf20Sopenharmony_ci		goto read_not_reliable;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	/* Check firmware version against what we downloaded. */
8518c2ecf20Sopenharmony_ci	if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) {
8528c2ecf20Sopenharmony_ci		if (!priv->ctrl.read_not_reliable) {
8538c2ecf20Sopenharmony_ci			tuner_err("Incorrect readback of firmware version.\n");
8548c2ecf20Sopenharmony_ci			goto fail;
8558c2ecf20Sopenharmony_ci		} else {
8568c2ecf20Sopenharmony_ci			tuner_err("Returned an incorrect version. However, read is not reliable enough. Ignoring it.\n");
8578c2ecf20Sopenharmony_ci			hwmodel = 3028;
8588c2ecf20Sopenharmony_ci		}
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	/* Check that the tuner hardware model remains consistent over time. */
8628c2ecf20Sopenharmony_ci	if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) {
8638c2ecf20Sopenharmony_ci		priv->hwmodel = hwmodel;
8648c2ecf20Sopenharmony_ci		priv->hwvers  = version & 0xff00;
8658c2ecf20Sopenharmony_ci	} else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
8668c2ecf20Sopenharmony_ci		   priv->hwvers != (version & 0xff00)) {
8678c2ecf20Sopenharmony_ci		tuner_err("Read invalid device hardware information - tuner hung?\n");
8688c2ecf20Sopenharmony_ci		goto fail;
8698c2ecf20Sopenharmony_ci	}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ciread_not_reliable:
8728c2ecf20Sopenharmony_ci	priv->cur_fw = new_fw;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	/*
8758c2ecf20Sopenharmony_ci	 * By setting BASE in cur_fw.type only after successfully loading all
8768c2ecf20Sopenharmony_ci	 * firmwares, we can:
8778c2ecf20Sopenharmony_ci	 * 1. Identify that BASE firmware with type=0 has been loaded;
8788c2ecf20Sopenharmony_ci	 * 2. Tell whether BASE firmware was just changed the next time through.
8798c2ecf20Sopenharmony_ci	 */
8808c2ecf20Sopenharmony_ci	priv->cur_fw.type |= BASE;
8818c2ecf20Sopenharmony_ci	priv->state = XC2028_ACTIVE;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	return 0;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_cifail:
8868c2ecf20Sopenharmony_ci	free_firmware(priv);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	if (retry_count < 8) {
8898c2ecf20Sopenharmony_ci		msleep(50);
8908c2ecf20Sopenharmony_ci		retry_count++;
8918c2ecf20Sopenharmony_ci		tuner_dbg("Retrying firmware load\n");
8928c2ecf20Sopenharmony_ci		goto retry;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	/* Firmware didn't load. Put the device to sleep */
8968c2ecf20Sopenharmony_ci	xc2028_sleep(fe);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	if (rc == -ENOENT)
8998c2ecf20Sopenharmony_ci		rc = -EINVAL;
9008c2ecf20Sopenharmony_ci	return rc;
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_cistatic int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
9068c2ecf20Sopenharmony_ci	u16                 frq_lock, signal = 0;
9078c2ecf20Sopenharmony_ci	int                 rc, i;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	rc = check_device_status(priv);
9128c2ecf20Sopenharmony_ci	if (rc < 0)
9138c2ecf20Sopenharmony_ci		return rc;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	/* If the device is sleeping, no channel is tuned */
9168c2ecf20Sopenharmony_ci	if (!rc) {
9178c2ecf20Sopenharmony_ci		*strength = 0;
9188c2ecf20Sopenharmony_ci		return 0;
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	/* Sync Lock Indicator */
9248c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
9258c2ecf20Sopenharmony_ci		rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
9268c2ecf20Sopenharmony_ci		if (rc < 0)
9278c2ecf20Sopenharmony_ci			goto ret;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci		if (frq_lock)
9308c2ecf20Sopenharmony_ci			break;
9318c2ecf20Sopenharmony_ci		msleep(6);
9328c2ecf20Sopenharmony_ci	}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	/* Frequency didn't lock */
9358c2ecf20Sopenharmony_ci	if (frq_lock == 2)
9368c2ecf20Sopenharmony_ci		goto ret;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	/* Get SNR of the video signal */
9398c2ecf20Sopenharmony_ci	rc = xc2028_get_reg(priv, XREG_SNR, &signal);
9408c2ecf20Sopenharmony_ci	if (rc < 0)
9418c2ecf20Sopenharmony_ci		goto ret;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/* Signal level is 3 bits only */
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	signal = ((1 << 12) - 1) | ((signal & 0x07) << 12);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ciret:
9488c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	*strength = signal;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	tuner_dbg("signal strength is %d\n", signal);
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	return rc;
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_cistatic int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
9608c2ecf20Sopenharmony_ci	int i, rc;
9618c2ecf20Sopenharmony_ci	u16 frq_lock = 0;
9628c2ecf20Sopenharmony_ci	s16 afc_reg = 0;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	rc = check_device_status(priv);
9658c2ecf20Sopenharmony_ci	if (rc < 0)
9668c2ecf20Sopenharmony_ci		return rc;
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	/* If the device is sleeping, no channel is tuned */
9698c2ecf20Sopenharmony_ci	if (!rc) {
9708c2ecf20Sopenharmony_ci		*afc = 0;
9718c2ecf20Sopenharmony_ci		return 0;
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	/* Sync Lock Indicator */
9778c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
9788c2ecf20Sopenharmony_ci		rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
9798c2ecf20Sopenharmony_ci		if (rc < 0)
9808c2ecf20Sopenharmony_ci			goto ret;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci		if (frq_lock)
9838c2ecf20Sopenharmony_ci			break;
9848c2ecf20Sopenharmony_ci		msleep(6);
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	/* Frequency didn't lock */
9888c2ecf20Sopenharmony_ci	if (frq_lock == 2)
9898c2ecf20Sopenharmony_ci		goto ret;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	/* Get AFC */
9928c2ecf20Sopenharmony_ci	rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg);
9938c2ecf20Sopenharmony_ci	if (rc < 0)
9948c2ecf20Sopenharmony_ci		goto ret;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	*afc = afc_reg * 15625; /* Hz */
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	tuner_dbg("AFC is %d Hz\n", *afc);
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ciret:
10018c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	return rc;
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci#define DIV 15625
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_cistatic int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
10098c2ecf20Sopenharmony_ci			    enum v4l2_tuner_type new_type,
10108c2ecf20Sopenharmony_ci			    unsigned int type,
10118c2ecf20Sopenharmony_ci			    v4l2_std_id std,
10128c2ecf20Sopenharmony_ci			    u16 int_freq)
10138c2ecf20Sopenharmony_ci{
10148c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
10158c2ecf20Sopenharmony_ci	int		   rc = -EINVAL;
10168c2ecf20Sopenharmony_ci	unsigned char	   buf[4];
10178c2ecf20Sopenharmony_ci	u32		   div, offset = 0;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	tuner_dbg("should set frequency %d kHz\n", freq / 1000);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	if (check_firmware(fe, type, std, int_freq) < 0)
10268c2ecf20Sopenharmony_ci		goto ret;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	/* On some cases xc2028 can disable video output, if
10298c2ecf20Sopenharmony_ci	 * very weak signals are received. By sending a soft
10308c2ecf20Sopenharmony_ci	 * reset, this is re-enabled. So, it is better to always
10318c2ecf20Sopenharmony_ci	 * send a soft reset before changing channels, to be sure
10328c2ecf20Sopenharmony_ci	 * that xc2028 will be in a safe state.
10338c2ecf20Sopenharmony_ci	 * Maybe this might also be needed for DTV.
10348c2ecf20Sopenharmony_ci	 */
10358c2ecf20Sopenharmony_ci	switch (new_type) {
10368c2ecf20Sopenharmony_ci	case V4L2_TUNER_ANALOG_TV:
10378c2ecf20Sopenharmony_ci		rc = send_seq(priv, {0x00, 0x00});
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci		/* Analog mode requires offset = 0 */
10408c2ecf20Sopenharmony_ci		break;
10418c2ecf20Sopenharmony_ci	case V4L2_TUNER_RADIO:
10428c2ecf20Sopenharmony_ci		/* Radio mode requires offset = 0 */
10438c2ecf20Sopenharmony_ci		break;
10448c2ecf20Sopenharmony_ci	case V4L2_TUNER_DIGITAL_TV:
10458c2ecf20Sopenharmony_ci		/*
10468c2ecf20Sopenharmony_ci		 * Digital modes require an offset to adjust to the
10478c2ecf20Sopenharmony_ci		 * proper frequency. The offset depends on what
10488c2ecf20Sopenharmony_ci		 * firmware version is used.
10498c2ecf20Sopenharmony_ci		 */
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci		/*
10528c2ecf20Sopenharmony_ci		 * Adjust to the center frequency. This is calculated by the
10538c2ecf20Sopenharmony_ci		 * formula: offset = 1.25MHz - BW/2
10548c2ecf20Sopenharmony_ci		 * For DTV 7/8, the firmware uses BW = 8000, so it needs a
10558c2ecf20Sopenharmony_ci		 * further adjustment to get the frequency center on VHF
10568c2ecf20Sopenharmony_ci		 */
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci		/*
10598c2ecf20Sopenharmony_ci		 * The firmware DTV78 used to work fine in UHF band (8 MHz
10608c2ecf20Sopenharmony_ci		 * bandwidth) but not at all in VHF band (7 MHz bandwidth).
10618c2ecf20Sopenharmony_ci		 * The real problem was connected to the formula used to
10628c2ecf20Sopenharmony_ci		 * calculate the center frequency offset in VHF band.
10638c2ecf20Sopenharmony_ci		 * In fact, removing the 500KHz adjustment fixed the problem.
10648c2ecf20Sopenharmony_ci		 * This is coherent to what was implemented for the DTV7
10658c2ecf20Sopenharmony_ci		 * firmware.
10668c2ecf20Sopenharmony_ci		 * In the end, now the center frequency is the same for all 3
10678c2ecf20Sopenharmony_ci		 * firmwares (DTV7, DTV8, DTV78) and doesn't depend on channel
10688c2ecf20Sopenharmony_ci		 * bandwidth.
10698c2ecf20Sopenharmony_ci		 */
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci		if (priv->cur_fw.type & DTV6)
10728c2ecf20Sopenharmony_ci			offset = 1750000;
10738c2ecf20Sopenharmony_ci		else	/* DTV7 or DTV8 or DTV78 */
10748c2ecf20Sopenharmony_ci			offset = 2750000;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci		/*
10778c2ecf20Sopenharmony_ci		 * xc3028 additional "magic"
10788c2ecf20Sopenharmony_ci		 * Depending on the firmware version, it needs some adjustments
10798c2ecf20Sopenharmony_ci		 * to properly centralize the frequency. This seems to be
10808c2ecf20Sopenharmony_ci		 * needed to compensate the SCODE table adjustments made by
10818c2ecf20Sopenharmony_ci		 * newer firmwares
10828c2ecf20Sopenharmony_ci		 */
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci		/*
10858c2ecf20Sopenharmony_ci		 * The proper adjustment would be to do it at s-code table.
10868c2ecf20Sopenharmony_ci		 * However, this didn't work, as reported by
10878c2ecf20Sopenharmony_ci		 * Robert Lowery <rglowery@exemail.com.au>
10888c2ecf20Sopenharmony_ci		 */
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci#if 0
10918c2ecf20Sopenharmony_ci		/*
10928c2ecf20Sopenharmony_ci		 * Still need tests for XC3028L (firmware 3.2 or upper)
10938c2ecf20Sopenharmony_ci		 * So, for now, let's just comment the per-firmware
10948c2ecf20Sopenharmony_ci		 * version of this change. Reports with xc3028l working
10958c2ecf20Sopenharmony_ci		 * with and without the lines below are welcome
10968c2ecf20Sopenharmony_ci		 */
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci		if (priv->firm_version < 0x0302) {
10998c2ecf20Sopenharmony_ci			if (priv->cur_fw.type & DTV7)
11008c2ecf20Sopenharmony_ci				offset += 500000;
11018c2ecf20Sopenharmony_ci		} else {
11028c2ecf20Sopenharmony_ci			if (priv->cur_fw.type & DTV7)
11038c2ecf20Sopenharmony_ci				offset -= 300000;
11048c2ecf20Sopenharmony_ci			else if (type != ATSC) /* DVB @6MHz, DTV 8 and DTV 7/8 */
11058c2ecf20Sopenharmony_ci				offset += 200000;
11068c2ecf20Sopenharmony_ci		}
11078c2ecf20Sopenharmony_ci#endif
11088c2ecf20Sopenharmony_ci		break;
11098c2ecf20Sopenharmony_ci	default:
11108c2ecf20Sopenharmony_ci		tuner_err("Unsupported tuner type %d.\n", new_type);
11118c2ecf20Sopenharmony_ci		break;
11128c2ecf20Sopenharmony_ci	}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	div = (freq - offset + DIV / 2) / DIV;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	/* CMD= Set frequency */
11178c2ecf20Sopenharmony_ci	if (priv->firm_version < 0x0202)
11188c2ecf20Sopenharmony_ci		rc = send_seq(priv, {0x00, XREG_RF_FREQ, 0x00, 0x00});
11198c2ecf20Sopenharmony_ci	else
11208c2ecf20Sopenharmony_ci		rc = send_seq(priv, {0x80, XREG_RF_FREQ, 0x00, 0x00});
11218c2ecf20Sopenharmony_ci	if (rc < 0)
11228c2ecf20Sopenharmony_ci		goto ret;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	/* Return code shouldn't be checked.
11258c2ecf20Sopenharmony_ci	   The reset CLK is needed only with tm6000.
11268c2ecf20Sopenharmony_ci	   Driver should work fine even if this fails.
11278c2ecf20Sopenharmony_ci	 */
11288c2ecf20Sopenharmony_ci	if (priv->ctrl.msleep)
11298c2ecf20Sopenharmony_ci		msleep(priv->ctrl.msleep);
11308c2ecf20Sopenharmony_ci	do_tuner_callback(fe, XC2028_RESET_CLK, 1);
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	msleep(10);
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	buf[0] = 0xff & (div >> 24);
11358c2ecf20Sopenharmony_ci	buf[1] = 0xff & (div >> 16);
11368c2ecf20Sopenharmony_ci	buf[2] = 0xff & (div >> 8);
11378c2ecf20Sopenharmony_ci	buf[3] = 0xff & (div);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	rc = i2c_send(priv, buf, sizeof(buf));
11408c2ecf20Sopenharmony_ci	if (rc < 0)
11418c2ecf20Sopenharmony_ci		goto ret;
11428c2ecf20Sopenharmony_ci	msleep(100);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	priv->frequency = freq;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	tuner_dbg("divisor= %*ph (freq=%d.%03d)\n", 4, buf,
11478c2ecf20Sopenharmony_ci	       freq / 1000000, (freq % 1000000) / 1000);
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	rc = 0;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ciret:
11528c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	return rc;
11558c2ecf20Sopenharmony_ci}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cistatic int xc2028_set_analog_freq(struct dvb_frontend *fe,
11588c2ecf20Sopenharmony_ci			      struct analog_parameters *p)
11598c2ecf20Sopenharmony_ci{
11608c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
11618c2ecf20Sopenharmony_ci	unsigned int       type=0;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	if (p->mode == V4L2_TUNER_RADIO) {
11668c2ecf20Sopenharmony_ci		type |= FM;
11678c2ecf20Sopenharmony_ci		if (priv->ctrl.input1)
11688c2ecf20Sopenharmony_ci			type |= INPUT1;
11698c2ecf20Sopenharmony_ci		return generic_set_freq(fe, (625l * p->frequency) / 10,
11708c2ecf20Sopenharmony_ci				V4L2_TUNER_RADIO, type, 0, 0);
11718c2ecf20Sopenharmony_ci	}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	/* if std is not defined, choose one */
11748c2ecf20Sopenharmony_ci	if (!p->std)
11758c2ecf20Sopenharmony_ci		p->std = V4L2_STD_MN;
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	/* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */
11788c2ecf20Sopenharmony_ci	if (!(p->std & V4L2_STD_MN))
11798c2ecf20Sopenharmony_ci		type |= F8MHZ;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	/* Add audio hack to std mask */
11828c2ecf20Sopenharmony_ci	p->std |= parse_audio_std_option();
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	return generic_set_freq(fe, 62500l * p->frequency,
11858c2ecf20Sopenharmony_ci				V4L2_TUNER_ANALOG_TV, type, p->std, 0);
11868c2ecf20Sopenharmony_ci}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_cistatic int xc2028_set_params(struct dvb_frontend *fe)
11898c2ecf20Sopenharmony_ci{
11908c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
11918c2ecf20Sopenharmony_ci	u32 delsys = c->delivery_system;
11928c2ecf20Sopenharmony_ci	u32 bw = c->bandwidth_hz;
11938c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
11948c2ecf20Sopenharmony_ci	int rc;
11958c2ecf20Sopenharmony_ci	unsigned int       type = 0;
11968c2ecf20Sopenharmony_ci	u16                demod = 0;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	rc = check_device_status(priv);
12018c2ecf20Sopenharmony_ci	if (rc < 0)
12028c2ecf20Sopenharmony_ci		return rc;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	switch (delsys) {
12058c2ecf20Sopenharmony_ci	case SYS_DVBT:
12068c2ecf20Sopenharmony_ci	case SYS_DVBT2:
12078c2ecf20Sopenharmony_ci		/*
12088c2ecf20Sopenharmony_ci		 * The only countries with 6MHz seem to be Taiwan/Uruguay.
12098c2ecf20Sopenharmony_ci		 * Both seem to require QAM firmware for OFDM decoding
12108c2ecf20Sopenharmony_ci		 * Tested in Taiwan by Terry Wu <terrywu2009@gmail.com>
12118c2ecf20Sopenharmony_ci		 */
12128c2ecf20Sopenharmony_ci		if (bw <= 6000000)
12138c2ecf20Sopenharmony_ci			type |= QAM;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci		switch (priv->ctrl.type) {
12168c2ecf20Sopenharmony_ci		case XC2028_D2633:
12178c2ecf20Sopenharmony_ci			type |= D2633;
12188c2ecf20Sopenharmony_ci			break;
12198c2ecf20Sopenharmony_ci		case XC2028_D2620:
12208c2ecf20Sopenharmony_ci			type |= D2620;
12218c2ecf20Sopenharmony_ci			break;
12228c2ecf20Sopenharmony_ci		case XC2028_AUTO:
12238c2ecf20Sopenharmony_ci		default:
12248c2ecf20Sopenharmony_ci			/* Zarlink seems to need D2633 */
12258c2ecf20Sopenharmony_ci			if (priv->ctrl.demod == XC3028_FE_ZARLINK456)
12268c2ecf20Sopenharmony_ci				type |= D2633;
12278c2ecf20Sopenharmony_ci			else
12288c2ecf20Sopenharmony_ci				type |= D2620;
12298c2ecf20Sopenharmony_ci		}
12308c2ecf20Sopenharmony_ci		break;
12318c2ecf20Sopenharmony_ci	case SYS_ATSC:
12328c2ecf20Sopenharmony_ci		/* The only ATSC firmware (at least on v2.7) is D2633 */
12338c2ecf20Sopenharmony_ci		type |= ATSC | D2633;
12348c2ecf20Sopenharmony_ci		break;
12358c2ecf20Sopenharmony_ci	/* DVB-S and pure QAM (FE_QAM) are not supported */
12368c2ecf20Sopenharmony_ci	default:
12378c2ecf20Sopenharmony_ci		return -EINVAL;
12388c2ecf20Sopenharmony_ci	}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	if (bw <= 6000000) {
12418c2ecf20Sopenharmony_ci		type |= DTV6;
12428c2ecf20Sopenharmony_ci		priv->ctrl.vhfbw7 = 0;
12438c2ecf20Sopenharmony_ci		priv->ctrl.uhfbw8 = 0;
12448c2ecf20Sopenharmony_ci	} else if (bw <= 7000000) {
12458c2ecf20Sopenharmony_ci		if (c->frequency < 470000000)
12468c2ecf20Sopenharmony_ci			priv->ctrl.vhfbw7 = 1;
12478c2ecf20Sopenharmony_ci		else
12488c2ecf20Sopenharmony_ci			priv->ctrl.uhfbw8 = 0;
12498c2ecf20Sopenharmony_ci		type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7;
12508c2ecf20Sopenharmony_ci		type |= F8MHZ;
12518c2ecf20Sopenharmony_ci	} else {
12528c2ecf20Sopenharmony_ci		if (c->frequency < 470000000)
12538c2ecf20Sopenharmony_ci			priv->ctrl.vhfbw7 = 0;
12548c2ecf20Sopenharmony_ci		else
12558c2ecf20Sopenharmony_ci			priv->ctrl.uhfbw8 = 1;
12568c2ecf20Sopenharmony_ci		type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8;
12578c2ecf20Sopenharmony_ci		type |= F8MHZ;
12588c2ecf20Sopenharmony_ci	}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	/* All S-code tables need a 200kHz shift */
12618c2ecf20Sopenharmony_ci	if (priv->ctrl.demod) {
12628c2ecf20Sopenharmony_ci		demod = priv->ctrl.demod;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci		/*
12658c2ecf20Sopenharmony_ci		 * Newer firmwares require a 200 kHz offset only for ATSC
12668c2ecf20Sopenharmony_ci		 */
12678c2ecf20Sopenharmony_ci		if (type == ATSC || priv->firm_version < 0x0302)
12688c2ecf20Sopenharmony_ci			demod += 200;
12698c2ecf20Sopenharmony_ci		/*
12708c2ecf20Sopenharmony_ci		 * The DTV7 S-code table needs a 700 kHz shift.
12718c2ecf20Sopenharmony_ci		 *
12728c2ecf20Sopenharmony_ci		 * DTV7 is only used in Australia.  Germany or Italy may also
12738c2ecf20Sopenharmony_ci		 * use this firmware after initialization, but a tune to a UHF
12748c2ecf20Sopenharmony_ci		 * channel should then cause DTV78 to be used.
12758c2ecf20Sopenharmony_ci		 *
12768c2ecf20Sopenharmony_ci		 * Unfortunately, on real-field tests, the s-code offset
12778c2ecf20Sopenharmony_ci		 * didn't work as expected, as reported by
12788c2ecf20Sopenharmony_ci		 * Robert Lowery <rglowery@exemail.com.au>
12798c2ecf20Sopenharmony_ci		 */
12808c2ecf20Sopenharmony_ci	}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	return generic_set_freq(fe, c->frequency,
12838c2ecf20Sopenharmony_ci				V4L2_TUNER_DIGITAL_TV, type, 0, demod);
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_cistatic int xc2028_sleep(struct dvb_frontend *fe)
12878c2ecf20Sopenharmony_ci{
12888c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
12898c2ecf20Sopenharmony_ci	int rc;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	rc = check_device_status(priv);
12928c2ecf20Sopenharmony_ci	if (rc < 0)
12938c2ecf20Sopenharmony_ci		return rc;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	/* Device is already in sleep mode */
12968c2ecf20Sopenharmony_ci	if (!rc)
12978c2ecf20Sopenharmony_ci		return 0;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	/* Avoid firmware reload on slow devices or if PM disabled */
13008c2ecf20Sopenharmony_ci	if (no_poweroff || priv->ctrl.disable_power_mgmt)
13018c2ecf20Sopenharmony_ci		return 0;
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	tuner_dbg("Putting xc2028/3028 into poweroff mode.\n");
13048c2ecf20Sopenharmony_ci	if (debug > 1) {
13058c2ecf20Sopenharmony_ci		tuner_dbg("Printing sleep stack trace:\n");
13068c2ecf20Sopenharmony_ci		dump_stack();
13078c2ecf20Sopenharmony_ci	}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	if (priv->firm_version < 0x0202)
13128c2ecf20Sopenharmony_ci		rc = send_seq(priv, {0x00, XREG_POWER_DOWN, 0x00, 0x00});
13138c2ecf20Sopenharmony_ci	else
13148c2ecf20Sopenharmony_ci		rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00});
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	if (rc >= 0)
13178c2ecf20Sopenharmony_ci		priv->state = XC2028_SLEEP;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	return rc;
13228c2ecf20Sopenharmony_ci}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_cistatic void xc2028_dvb_release(struct dvb_frontend *fe)
13258c2ecf20Sopenharmony_ci{
13268c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	mutex_lock(&xc2028_list_mutex);
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	/* only perform final cleanup if this is the last instance */
13338c2ecf20Sopenharmony_ci	if (hybrid_tuner_report_instance_count(priv) == 1)
13348c2ecf20Sopenharmony_ci		free_firmware(priv);
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	if (priv)
13378c2ecf20Sopenharmony_ci		hybrid_tuner_release_state(priv);
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	mutex_unlock(&xc2028_list_mutex);
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	fe->tuner_priv = NULL;
13428c2ecf20Sopenharmony_ci}
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_cistatic int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
13458c2ecf20Sopenharmony_ci{
13468c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
13478c2ecf20Sopenharmony_ci	int rc;
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	rc = check_device_status(priv);
13528c2ecf20Sopenharmony_ci	if (rc < 0)
13538c2ecf20Sopenharmony_ci		return rc;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	*frequency = priv->frequency;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	return 0;
13588c2ecf20Sopenharmony_ci}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_cistatic void load_firmware_cb(const struct firmware *fw,
13618c2ecf20Sopenharmony_ci			     void *context)
13628c2ecf20Sopenharmony_ci{
13638c2ecf20Sopenharmony_ci	struct dvb_frontend *fe = context;
13648c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
13658c2ecf20Sopenharmony_ci	int rc;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error");
13688c2ecf20Sopenharmony_ci	if (!fw) {
13698c2ecf20Sopenharmony_ci		tuner_err("Could not load firmware %s.\n", priv->fname);
13708c2ecf20Sopenharmony_ci		priv->state = XC2028_NODEV;
13718c2ecf20Sopenharmony_ci		return;
13728c2ecf20Sopenharmony_ci	}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	rc = load_all_firmwares(fe, fw);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	release_firmware(fw);
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	if (rc < 0)
13798c2ecf20Sopenharmony_ci		return;
13808c2ecf20Sopenharmony_ci	priv->state = XC2028_ACTIVE;
13818c2ecf20Sopenharmony_ci}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
13848c2ecf20Sopenharmony_ci{
13858c2ecf20Sopenharmony_ci	struct xc2028_data *priv = fe->tuner_priv;
13868c2ecf20Sopenharmony_ci	struct xc2028_ctrl *p    = priv_cfg;
13878c2ecf20Sopenharmony_ci	int                 rc   = 0;
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	tuner_dbg("%s called\n", __func__);
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	mutex_lock(&priv->lock);
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	/*
13948c2ecf20Sopenharmony_ci	 * Copy the config data.
13958c2ecf20Sopenharmony_ci	 */
13968c2ecf20Sopenharmony_ci	memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	/*
13998c2ecf20Sopenharmony_ci	 * If firmware name changed, frees firmware. As free_firmware will
14008c2ecf20Sopenharmony_ci	 * reset the status to NO_FIRMWARE, this forces a new request_firmware
14018c2ecf20Sopenharmony_ci	 */
14028c2ecf20Sopenharmony_ci	if (!firmware_name[0] && p->fname &&
14038c2ecf20Sopenharmony_ci	    priv->fname && strcmp(p->fname, priv->fname))
14048c2ecf20Sopenharmony_ci		free_firmware(priv);
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	if (priv->ctrl.max_len < 9)
14078c2ecf20Sopenharmony_ci		priv->ctrl.max_len = 13;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	if (priv->state == XC2028_NO_FIRMWARE) {
14108c2ecf20Sopenharmony_ci		if (!firmware_name[0])
14118c2ecf20Sopenharmony_ci			priv->fname = kstrdup(p->fname, GFP_KERNEL);
14128c2ecf20Sopenharmony_ci		else
14138c2ecf20Sopenharmony_ci			priv->fname = firmware_name;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci		if (!priv->fname) {
14168c2ecf20Sopenharmony_ci			rc = -ENOMEM;
14178c2ecf20Sopenharmony_ci			goto unlock;
14188c2ecf20Sopenharmony_ci		}
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci		rc = request_firmware_nowait(THIS_MODULE, 1,
14218c2ecf20Sopenharmony_ci					     priv->fname,
14228c2ecf20Sopenharmony_ci					     priv->i2c_props.adap->dev.parent,
14238c2ecf20Sopenharmony_ci					     GFP_KERNEL,
14248c2ecf20Sopenharmony_ci					     fe, load_firmware_cb);
14258c2ecf20Sopenharmony_ci		if (rc < 0) {
14268c2ecf20Sopenharmony_ci			tuner_err("Failed to request firmware %s\n",
14278c2ecf20Sopenharmony_ci				  priv->fname);
14288c2ecf20Sopenharmony_ci			priv->state = XC2028_NODEV;
14298c2ecf20Sopenharmony_ci		} else
14308c2ecf20Sopenharmony_ci			priv->state = XC2028_WAITING_FIRMWARE;
14318c2ecf20Sopenharmony_ci	}
14328c2ecf20Sopenharmony_ciunlock:
14338c2ecf20Sopenharmony_ci	mutex_unlock(&priv->lock);
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	return rc;
14368c2ecf20Sopenharmony_ci}
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
14398c2ecf20Sopenharmony_ci	.info = {
14408c2ecf20Sopenharmony_ci		 .name = "Xceive XC3028",
14418c2ecf20Sopenharmony_ci		 .frequency_min_hz  =  42 * MHz,
14428c2ecf20Sopenharmony_ci		 .frequency_max_hz  = 864 * MHz,
14438c2ecf20Sopenharmony_ci		 .frequency_step_hz =  50 * kHz,
14448c2ecf20Sopenharmony_ci		 },
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	.set_config	   = xc2028_set_config,
14478c2ecf20Sopenharmony_ci	.set_analog_params = xc2028_set_analog_freq,
14488c2ecf20Sopenharmony_ci	.release           = xc2028_dvb_release,
14498c2ecf20Sopenharmony_ci	.get_frequency     = xc2028_get_frequency,
14508c2ecf20Sopenharmony_ci	.get_rf_strength   = xc2028_signal,
14518c2ecf20Sopenharmony_ci	.get_afc           = xc2028_get_afc,
14528c2ecf20Sopenharmony_ci	.set_params        = xc2028_set_params,
14538c2ecf20Sopenharmony_ci	.sleep             = xc2028_sleep,
14548c2ecf20Sopenharmony_ci};
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_cistruct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
14578c2ecf20Sopenharmony_ci				   struct xc2028_config *cfg)
14588c2ecf20Sopenharmony_ci{
14598c2ecf20Sopenharmony_ci	struct xc2028_data *priv;
14608c2ecf20Sopenharmony_ci	int instance;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	if (debug)
14638c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n");
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	if (NULL == cfg)
14668c2ecf20Sopenharmony_ci		return NULL;
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	if (!fe) {
14698c2ecf20Sopenharmony_ci		printk(KERN_ERR "xc2028: No frontend!\n");
14708c2ecf20Sopenharmony_ci		return NULL;
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	mutex_lock(&xc2028_list_mutex);
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	instance = hybrid_tuner_request_state(struct xc2028_data, priv,
14768c2ecf20Sopenharmony_ci					      hybrid_tuner_instance_list,
14778c2ecf20Sopenharmony_ci					      cfg->i2c_adap, cfg->i2c_addr,
14788c2ecf20Sopenharmony_ci					      "xc2028");
14798c2ecf20Sopenharmony_ci	switch (instance) {
14808c2ecf20Sopenharmony_ci	case 0:
14818c2ecf20Sopenharmony_ci		/* memory allocation failure */
14828c2ecf20Sopenharmony_ci		goto fail;
14838c2ecf20Sopenharmony_ci	case 1:
14848c2ecf20Sopenharmony_ci		/* new tuner instance */
14858c2ecf20Sopenharmony_ci		priv->ctrl.max_len = 13;
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci		mutex_init(&priv->lock);
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci		fe->tuner_priv = priv;
14908c2ecf20Sopenharmony_ci		break;
14918c2ecf20Sopenharmony_ci	case 2:
14928c2ecf20Sopenharmony_ci		/* existing tuner instance */
14938c2ecf20Sopenharmony_ci		fe->tuner_priv = priv;
14948c2ecf20Sopenharmony_ci		break;
14958c2ecf20Sopenharmony_ci	}
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
14988c2ecf20Sopenharmony_ci	       sizeof(xc2028_dvb_tuner_ops));
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	if (cfg->ctrl)
15038c2ecf20Sopenharmony_ci		xc2028_set_config(fe, cfg->ctrl);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	mutex_unlock(&xc2028_list_mutex);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	return fe;
15088c2ecf20Sopenharmony_cifail:
15098c2ecf20Sopenharmony_ci	mutex_unlock(&xc2028_list_mutex);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	xc2028_dvb_release(fe);
15128c2ecf20Sopenharmony_ci	return NULL;
15138c2ecf20Sopenharmony_ci}
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xc2028_attach);
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
15188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
15198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@kernel.org>");
15208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
15218c2ecf20Sopenharmony_ciMODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
15228c2ecf20Sopenharmony_ciMODULE_FIRMWARE(XC3028L_DEFAULT_FIRMWARE);
1523