tuner-xc2028.c

来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,287 行 · 第 1/2 页

C
1,287
字号
/* tuner-xc2028 * * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) * * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com) *       - frontend interface * * This code is placed under the terms of the GNU General Public License v2 */#include <linux/i2c.h>#include <asm/div64.h>#include <linux/firmware.h>#include <linux/videodev2.h>#include <linux/delay.h>#include <media/tuner.h>#include <linux/mutex.h>#include "compat.h"#include <asm/unaligned.h>#include "tuner-i2c.h"#include "tuner-xc2028.h"#include "tuner-xc2028-types.h"#include <linux/dvb/frontend.h>#include "dvb_frontend.h"#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)#define strcasecmp(a, b) strnicmp(a, b, sizeof(a))#endifstatic int debug;module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "enable verbose debug messages");static char audio_std[8];module_param_string(audio_std, audio_std, sizeof(audio_std), 0);MODULE_PARM_DESC(audio_std,	"Audio standard. XC3028 audio decoder explicitly "	"needs to know what audio\n"	"standard is needed for some video standards with audio A2 or NICAM.\n"	"The valid values are:\n"	"A2\n"	"A2/A\n"	"A2/B\n"	"NICAM\n"	"NICAM/A\n"	"NICAM/B\n");static char firmware_name[FIRMWARE_NAME_MAX];module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "				"default firmware name\n");static LIST_HEAD(hybrid_tuner_instance_list);static DEFINE_MUTEX(xc2028_list_mutex);/* struct for storing firmware table */struct firmware_description {	unsigned int  type;	v4l2_std_id   id;	__u16         int_freq;	unsigned char *ptr;	unsigned int  size;};struct firmware_properties {	unsigned int	type;	v4l2_std_id	id;	v4l2_std_id	std_req;	__u16		int_freq;	unsigned int	scode_table;	int 		scode_nr;};struct xc2028_data {	struct list_head        hybrid_tuner_instance_list;	struct tuner_i2c_props  i2c_props;	__u32			frequency;	struct firmware_description *firm;	int			firm_size;	__u16			firm_version;	__u16			hwmodel;	__u16			hwvers;	struct xc2028_ctrl	ctrl;	struct firmware_properties cur_fw;	struct mutex lock;};#define i2c_send(priv, buf, size) ({					\	int _rc;							\	_rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size);		\	if (size != _rc)						\		tuner_info("i2c output error: rc = %d (should be %d)\n",\			   _rc, (int)size);				\	msleep(priv->ctrl.msleep);					\	_rc;								\})#define i2c_rcv(priv, buf, size) ({					\	int _rc;							\	_rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size);		\	if (size != _rc)						\		tuner_err("i2c input error: rc = %d (should be %d)\n",	\			   _rc, (int)size); 				\	_rc;								\})#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({		\	int _rc;							\	_rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize,	\				       ibuf, isize);			\	if (isize != _rc)						\		tuner_err("i2c input error: rc = %d (should be %d)\n",	\			   _rc, (int)isize); 				\	msleep(priv->ctrl.msleep);					\	_rc;								\})#define send_seq(priv, data...)	({					\	static u8 _val[] = data;					\	int _rc;							\	if (sizeof(_val) !=						\			(_rc = tuner_i2c_xfer_send(&priv->i2c_props,	\						_val, sizeof(_val)))) {	\		tuner_err("Error on line %d: %d\n", __LINE__, _rc);	\	} else 								\		msleep(priv->ctrl.msleep);				\	_rc;								\})static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val){	unsigned char buf[2];	unsigned char ibuf[2];	tuner_dbg("%s %04x called\n", __func__, reg);	buf[0] = reg >> 8;	buf[1] = (unsigned char) reg;	if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)		return -EIO;	*val = (ibuf[1]) | (ibuf[0] << 8);	return 0;}#define dump_firm_type(t) 	dump_firm_type_and_int_freq(t, 0)static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq){	 if (type & BASE)		printk("BASE ");	 if (type & INIT1)		printk("INIT1 ");	 if (type & F8MHZ)		printk("F8MHZ ");	 if (type & MTS)		printk("MTS ");	 if (type & D2620)		printk("D2620 ");	 if (type & D2633)		printk("D2633 ");	 if (type & DTV6)		printk("DTV6 ");	 if (type & QAM)		printk("QAM ");	 if (type & DTV7)		printk("DTV7 ");	 if (type & DTV78)		printk("DTV78 ");	 if (type & DTV8)		printk("DTV8 ");	 if (type & FM)		printk("FM ");	 if (type & INPUT1)		printk("INPUT1 ");	 if (type & LCD)		printk("LCD ");	 if (type & NOGD)		printk("NOGD ");	 if (type & MONO)		printk("MONO ");	 if (type & ATSC)		printk("ATSC ");	 if (type & IF)		printk("IF ");	 if (type & LG60)		printk("LG60 ");	 if (type & ATI638)		printk("ATI638 ");	 if (type & OREN538)		printk("OREN538 ");	 if (type & OREN36)		printk("OREN36 ");	 if (type & TOYOTA388)		printk("TOYOTA388 ");	 if (type & TOYOTA794)		printk("TOYOTA794 ");	 if (type & DIBCOM52)		printk("DIBCOM52 ");	 if (type & ZARLINK456)		printk("ZARLINK456 ");	 if (type & CHINA)		printk("CHINA ");	 if (type & F6MHZ)		printk("F6MHZ ");	 if (type & INPUT2)		printk("INPUT2 ");	 if (type & SCODE)		printk("SCODE ");	 if (type & HAS_IF)		printk("HAS_IF_%d ", int_freq);}static  v4l2_std_id parse_audio_std_option(void){	if (strcasecmp(audio_std, "A2") == 0)		return V4L2_STD_A2;	if (strcasecmp(audio_std, "A2/A") == 0)		return V4L2_STD_A2_A;	if (strcasecmp(audio_std, "A2/B") == 0)		return V4L2_STD_A2_B;	if (strcasecmp(audio_std, "NICAM") == 0)		return V4L2_STD_NICAM;	if (strcasecmp(audio_std, "NICAM/A") == 0)		return V4L2_STD_NICAM_A;	if (strcasecmp(audio_std, "NICAM/B") == 0)		return V4L2_STD_NICAM_B;	return 0;}static void free_firmware(struct xc2028_data *priv){	int i;	tuner_dbg("%s called\n", __func__);	if (!priv->firm)		return;	for (i = 0; i < priv->firm_size; i++)		kfree(priv->firm[i].ptr);	kfree(priv->firm);	priv->firm = NULL;	priv->firm_size = 0;	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));}static int load_all_firmwares(struct dvb_frontend *fe){	struct xc2028_data    *priv = fe->tuner_priv;	const struct firmware *fw   = NULL;	const unsigned char   *p, *endp;	int                   rc = 0;	int		      n, n_array;	char		      name[33];	char		      *fname;	tuner_dbg("%s called\n", __func__);	if (!firmware_name[0])		fname = priv->ctrl.fname;	else		fname = firmware_name;	tuner_dbg("Reading firmware %s\n", fname);	rc = request_firmware(&fw, fname, &priv->i2c_props.adap->dev);	if (rc < 0) {		if (rc == -ENOENT)			tuner_err("Error: firmware %s not found.\n",				   fname);		else			tuner_err("Error %d while requesting firmware %s \n",				   rc, fname);		return rc;	}	p = fw->data;	endp = p + fw->size;	if (fw->size < sizeof(name) - 1 + 2 + 2) {		tuner_err("Error: firmware file %s has invalid size!\n",			  fname);		goto corrupt;	}	memcpy(name, p, sizeof(name) - 1);	name[sizeof(name) - 1] = 0;	p += sizeof(name) - 1;	priv->firm_version = get_unaligned_le16(p);	p += 2;	n_array = get_unaligned_le16(p);	p += 2;	tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",		   n_array, fname, name,		   priv->firm_version >> 8, priv->firm_version & 0xff);	priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL);	if (priv->firm == NULL) {		tuner_err("Not enough memory to load firmware file.\n");		rc = -ENOMEM;		goto err;	}	priv->firm_size = n_array;	n = -1;	while (p < endp) {		__u32 type, size;		v4l2_std_id id;		__u16 int_freq = 0;		n++;		if (n >= n_array) {			tuner_err("More firmware images in file than "				  "were expected!\n");			goto corrupt;		}		/* Checks if there's enough bytes to read */		if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))			goto header;		type = get_unaligned_le32(p);		p += sizeof(type);		id = get_unaligned_le64(p);		p += sizeof(id);		if (type & HAS_IF) {			int_freq = get_unaligned_le16(p);			p += sizeof(int_freq);			if (endp - p < sizeof(size))				goto header;		}		size = get_unaligned_le32(p);		p += sizeof(size);		if (!size || size > endp - p) {			tuner_err("Firmware type ");			dump_firm_type(type);			printk("(%x), id %llx is corrupted "			       "(size=%d, expected %d)\n",			       type, (unsigned long long)id,			       (unsigned)(endp - p), size);			goto corrupt;		}		priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);		if (priv->firm[n].ptr == NULL) {			tuner_err("Not enough memory to load firmware file.\n");			rc = -ENOMEM;			goto err;		}		tuner_dbg("Reading firmware type ");		if (debug) {			dump_firm_type_and_int_freq(type, int_freq);			printk("(%x), id %llx, size=%d.\n",			       type, (unsigned long long)id, size);		}		memcpy(priv->firm[n].ptr, p, size);		priv->firm[n].type = type;		priv->firm[n].id   = id;		priv->firm[n].size = size;		priv->firm[n].int_freq = int_freq;		p += size;	}	if (n + 1 != priv->firm_size) {		tuner_err("Firmware file is incomplete!\n");		goto corrupt;	}	goto done;header:	tuner_err("Firmware header is incomplete!\n");corrupt:	rc = -EINVAL;	tuner_err("Error: firmware file is corrupted!\n");err:	tuner_info("Releasing partially loaded firmware file.\n");	free_firmware(priv);done:	release_firmware(fw);	if (rc == 0)		tuner_dbg("Firmware files loaded.\n");	return rc;}static int seek_firmware(struct dvb_frontend *fe, unsigned int type,			 v4l2_std_id *id){	struct xc2028_data *priv = fe->tuner_priv;	int                 i, best_i = -1, best_nr_matches = 0;	unsigned int        type_mask = 0;	tuner_dbg("%s called, want type=", __func__);	if (debug) {		dump_firm_type(type);		printk("(%x), id %016llx.\n", type, (unsigned long long)*id);	}	if (!priv->firm) {		tuner_err("Error! firmware not loaded\n");		return -EINVAL;	}	if (((type & ~SCODE) == 0) && (*id == 0))		*id = V4L2_STD_PAL;	if (type & BASE)		type_mask = BASE_TYPES;	else if (type & SCODE) {		type &= SCODE_TYPES;		type_mask = SCODE_TYPES & ~HAS_IF;	} else if (type & DTV_TYPES)		type_mask = DTV_TYPES;	else if (type & STD_SPECIFIC_TYPES)		type_mask = STD_SPECIFIC_TYPES;	type &= type_mask;	if (!(type & SCODE))		type_mask = ~0;	/* Seek for exact match */	for (i = 0; i < priv->firm_size; i++) {		if ((type == (priv->firm[i].type & type_mask)) &&		    (*id == priv->firm[i].id))			goto found;	}	/* Seek for generic video standard match */	for (i = 0; i < priv->firm_size; i++) {		v4l2_std_id match_mask;		int nr_matches;		if (type != (priv->firm[i].type & type_mask))			continue;		match_mask = *id & priv->firm[i].id;		if (!match_mask)			continue;		if ((*id & match_mask) == *id)			goto found; /* Supports all the requested standards */		nr_matches = hweight64(match_mask);		if (nr_matches > best_nr_matches) {			best_nr_matches = nr_matches;			best_i = i;		}	}	if (best_nr_matches > 0) {		tuner_dbg("Selecting best matching firmware (%d bits) for "			  "type=", best_nr_matches);		dump_firm_type(type);		printk("(%x), id %016llx:\n", type, (unsigned long long)*id);		i = best_i;		goto found;	}	/*FIXME: Would make sense to seek for type "hint" match ? */	i = -ENOENT;	goto ret;found:	*id = priv->firm[i].id;ret:	tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found");	if (debug) {		dump_firm_type(type);		printk("(%x), id %016llx.\n", type, (unsigned long long)*id);	}	return i;}static inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg){	struct xc2028_data *priv = fe->tuner_priv;	/* analog side (tuner-core) uses i2c_adap->algo_data.	 * digital side is not guaranteed to have algo_data defined.	 *	 * digital side will always have fe->dvb defined.	 * analog side (tuner-core) doesn't (yet) define fe->dvb.	 */	return (!fe->callback) ? -EINVAL :		fe->callback(((fe->dvb) && (fe->dvb->priv)) ?				fe->dvb->priv : priv->i2c_props.adap->algo_data,			     DVB_FRONTEND_COMPONENT_TUNER, cmd, arg);}static int load_firmware(struct dvb_frontend *fe, unsigned int type,			 v4l2_std_id *id){	struct xc2028_data *priv = fe->tuner_priv;	int                pos, rc;	unsigned char      *p, *endp, buf[priv->ctrl.max_len];	tuner_dbg("%s called\n", __func__);	pos = seek_firmware(fe, type, id);	if (pos < 0)		return pos;	tuner_info("Loading firmware for type=");	dump_firm_type(priv->firm[pos].type);	printk("(%x), id %016llx.\n", priv->firm[pos].type,	       (unsigned long long)*id);	p = priv->firm[pos].ptr;	endp = p + priv->firm[pos].size;	while (p < endp) {		__u16 size;		/* Checks if there's enough bytes to read */		if (p + sizeof(size) > endp) {			tuner_err("Firmware chunk size is wrong\n");			return -EINVAL;		}		size = le16_to_cpu(*(__u16 *) p);		p += sizeof(size);		if (size == 0xffff)			return 0;		if (!size) {			/* Special callback command received */			rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);			if (rc < 0) {				tuner_err("Error at RESET code %d\n",					   (*p) & 0x7f);				return -EINVAL;			}			continue;		}		if (size >= 0xff00) {			switch (size) {			case 0xff00:				rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0);				if (rc < 0) {					tuner_err("Error at RESET code %d\n",						  (*p) & 0x7f);					return -EINVAL;				}				break;			default:				tuner_info("Invalid RESET code %d\n",					   size & 0x7f);				return -EINVAL;			}			continue;		}		/* Checks for a sleep command */		if (size & 0x8000) {			msleep(size & 0x7fff);			continue;		}		if ((size + p > endp)) {			tuner_err("missing bytes: need %d, have %d\n",				   size, (int)(endp - p));			return -EINVAL;		}		buf[0] = *p;		p++;		size--;		/* Sends message chunks */		while (size > 0) {			int len = (size < priv->ctrl.max_len - 1) ?				   size : priv->ctrl.max_len - 1;			memcpy(buf + 1, p, len);			rc = i2c_send(priv, buf, len + 1);			if (rc < 0) {				tuner_err("%d returned from send\n", rc);				return -EINVAL;			}			p += len;			size -= len;		}	}	return 0;}static int load_scode(struct dvb_frontend *fe, unsigned int type,			 v4l2_std_id *id, __u16 int_freq, int scode){	struct xc2028_data *priv = fe->tuner_priv;	int                pos, rc;	unsigned char	   *p;	tuner_dbg("%s called\n", __func__);	if (!int_freq) {		pos = seek_firmware(fe, type, id);		if (pos < 0)			return pos;	} else {		for (pos = 0; pos < priv->firm_size; pos++) {			if ((priv->firm[pos].int_freq == int_freq) &&			    (priv->firm[pos].type & HAS_IF))				break;		}		if (pos == priv->firm_size)			return -ENOENT;	}	p = priv->firm[pos].ptr;	if (priv->firm[pos].type & HAS_IF) {		if (priv->firm[pos].size != 12 * 16 || scode >= 16)			return -EINVAL;		p += 12 * scode;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?