tuner-xc2028.c

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

C
1,287
字号
	} else {		/* 16 SCODE entries per file; each SCODE entry is 12 bytes and		 * has a 2-byte size header in the firmware format. */		if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||		    le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12)			return -EINVAL;		p += 14 * scode + 2;	}	tuner_info("Loading SCODE for type=");	dump_firm_type_and_int_freq(priv->firm[pos].type,				    priv->firm[pos].int_freq);	printk("(%x), id %016llx.\n", priv->firm[pos].type,	       (unsigned long long)*id);	if (priv->firm_version < 0x0202)		rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00});	else		rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00});	if (rc < 0)		return -EIO;	rc = i2c_send(priv, p, 12);	if (rc < 0)		return -EIO;	rc = send_seq(priv, {0x00, 0x8c});	if (rc < 0)		return -EIO;	return 0;}static int check_firmware(struct dvb_frontend *fe, unsigned int type,			  v4l2_std_id std, __u16 int_freq){	struct xc2028_data         *priv = fe->tuner_priv;	struct firmware_properties new_fw;	int			   rc = 0, is_retry = 0;	u16			   version, hwmodel;	v4l2_std_id		   std0;	tuner_dbg("%s called\n", __func__);	if (!priv->firm) {		if (!priv->ctrl.fname) {			tuner_info("xc2028/3028 firmware name not set!\n");			return -EINVAL;		}		rc = load_all_firmwares(fe);		if (rc < 0)			return rc;	}	if (priv->ctrl.mts && !(type & FM))		type |= MTS;retry:	new_fw.type = type;	new_fw.id = std;	new_fw.std_req = std;	new_fw.scode_table = SCODE | priv->ctrl.scode_table;	new_fw.scode_nr = 0;	new_fw.int_freq = int_freq;	tuner_dbg("checking firmware, user requested type=");	if (debug) {		dump_firm_type(new_fw.type);		printk("(%x), id %016llx, ", new_fw.type,		       (unsigned long long)new_fw.std_req);		if (!int_freq) {			printk("scode_tbl ");			dump_firm_type(priv->ctrl.scode_table);			printk("(%x), ", priv->ctrl.scode_table);		} else			printk("int_freq %d, ", new_fw.int_freq);		printk("scode_nr %d\n", new_fw.scode_nr);	}	/* No need to reload base firmware if it matches */	if (((BASE | new_fw.type) & BASE_TYPES) ==	    (priv->cur_fw.type & BASE_TYPES)) {		tuner_dbg("BASE firmware not changed.\n");		goto skip_base;	}	/* Updating BASE - forget about all currently loaded firmware */	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));	/* Reset is needed before loading firmware */	rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);	if (rc < 0)		goto fail;	/* BASE firmwares are all std0 */	std0 = 0;	rc = load_firmware(fe, BASE | new_fw.type, &std0);	if (rc < 0) {		tuner_err("Error %d while loading base firmware\n",			  rc);		goto fail;	}	/* Load INIT1, if needed */	tuner_dbg("Load init1 firmware, if exists\n");	rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0);	if (rc == -ENOENT)		rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ,				   &std0);	if (rc < 0 && rc != -ENOENT) {		tuner_err("Error %d while loading init1 firmware\n",			  rc);		goto fail;	}skip_base:	/*	 * No need to reload standard specific firmware if base firmware	 * was not reloaded and requested video standards have not changed.	 */	if (priv->cur_fw.type == (BASE | new_fw.type) &&	    priv->cur_fw.std_req == std) {		tuner_dbg("Std-specific firmware already loaded.\n");		goto skip_std_specific;	}	/* Reloading std-specific firmware forces a SCODE update */	priv->cur_fw.scode_table = 0;	rc = load_firmware(fe, new_fw.type, &new_fw.id);	if (rc == -ENOENT)		rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id);	if (rc < 0)		goto fail;skip_std_specific:	if (priv->cur_fw.scode_table == new_fw.scode_table &&	    priv->cur_fw.scode_nr == new_fw.scode_nr) {		tuner_dbg("SCODE firmware already loaded.\n");		goto check_device;	}	if (new_fw.type & FM)		goto check_device;	/* Load SCODE firmware, if exists */	tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);	rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,			new_fw.int_freq, new_fw.scode_nr);check_device:	if (xc2028_get_reg(priv, 0x0004, &version) < 0 ||	    xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) {		tuner_err("Unable to read tuner registers.\n");		goto fail;	}	tuner_dbg("Device is Xceive %d version %d.%d, "		  "firmware version %d.%d\n",		  hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,		  (version & 0xf0) >> 4, version & 0xf);	/* Check firmware version against what we downloaded. */	if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) {		if (!priv->ctrl.read_not_reliable) {			tuner_err("Incorrect readback of firmware version.\n");			goto fail;		} else {			tuner_err("Returned an incorrect version. However, "				  "read is not reliable enough. Ignoring it.\n");			hwmodel = 3028;		}	}	/* Check that the tuner hardware model remains consistent over time. */	if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) {		priv->hwmodel = hwmodel;		priv->hwvers  = version & 0xff00;	} else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||		   priv->hwvers != (version & 0xff00)) {		tuner_err("Read invalid device hardware information - tuner "			  "hung?\n");		goto fail;	}	memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));	/*	 * By setting BASE in cur_fw.type only after successfully loading all	 * firmwares, we can:	 * 1. Identify that BASE firmware with type=0 has been loaded;	 * 2. Tell whether BASE firmware was just changed the next time through.	 */	priv->cur_fw.type |= BASE;	return 0;fail:	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));	if (!is_retry) {		msleep(50);		is_retry = 1;		tuner_dbg("Retrying firmware load\n");		goto retry;	}	if (rc == -ENOENT)		rc = -EINVAL;	return rc;}static int xc2028_signal(struct dvb_frontend *fe, u16 *strength){	struct xc2028_data *priv = fe->tuner_priv;	u16                 frq_lock, signal = 0;	int                 rc;	tuner_dbg("%s called\n", __func__);	mutex_lock(&priv->lock);	/* Sync Lock Indicator */	rc = xc2028_get_reg(priv, 0x0002, &frq_lock);	if (rc < 0)		goto ret;	/* Frequency is locked */	if (frq_lock == 1)		signal = 32768;	/* Get SNR of the video signal */	rc = xc2028_get_reg(priv, 0x0040, &signal);	if (rc < 0)		goto ret;	/* Use both frq_lock and signal to generate the result */	signal = signal || ((signal & 0x07) << 12);ret:	mutex_unlock(&priv->lock);	*strength = signal;	tuner_dbg("signal strength is %d\n", signal);	return rc;}#define DIV 15625static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,			    enum tuner_mode new_mode,			    unsigned int type,			    v4l2_std_id std,			    u16 int_freq){	struct xc2028_data *priv = fe->tuner_priv;	int		   rc = -EINVAL;	unsigned char	   buf[4];	u32		   div, offset = 0;	tuner_dbg("%s called\n", __func__);	mutex_lock(&priv->lock);	tuner_dbg("should set frequency %d kHz\n", freq / 1000);	if (check_firmware(fe, type, std, int_freq) < 0)		goto ret;	/* On some cases xc2028 can disable video output, if	 * very weak signals are received. By sending a soft	 * reset, this is re-enabled. So, it is better to always	 * send a soft reset before changing channels, to be sure	 * that xc2028 will be in a safe state.	 * Maybe this might also be needed for DTV.	 */	if (new_mode == T_ANALOG_TV) {		rc = send_seq(priv, {0x00, 0x00});	} else if (priv->cur_fw.type & ATSC) {		offset = 1750000;	} else {		offset = 2750000;		/*		 * We must adjust the offset by 500kHz in two cases in order		 * to correctly center the IF output:		 * 1) When the ZARLINK456 or DIBCOM52 tables were explicitly		 *    selected and a 7MHz channel is tuned;		 * 2) When tuning a VHF channel with DTV78 firmware.		 */		if (((priv->cur_fw.type & DTV7) &&		     (priv->cur_fw.scode_table & (ZARLINK456 | DIBCOM52))) ||		    ((priv->cur_fw.type & DTV78) && freq < 470000000))			offset -= 500000;	}	div = (freq - offset + DIV / 2) / DIV;	/* CMD= Set frequency */	if (priv->firm_version < 0x0202)		rc = send_seq(priv, {0x00, 0x02, 0x00, 0x00});	else		rc = send_seq(priv, {0x80, 0x02, 0x00, 0x00});	if (rc < 0)		goto ret;	/* Return code shouldn't be checked.	   The reset CLK is needed only with tm6000.	   Driver should work fine even if this fails.	 */	msleep(priv->ctrl.msleep);	do_tuner_callback(fe, XC2028_RESET_CLK, 1);	msleep(10);	buf[0] = 0xff & (div >> 24);	buf[1] = 0xff & (div >> 16);	buf[2] = 0xff & (div >> 8);	buf[3] = 0xff & (div);	rc = i2c_send(priv, buf, sizeof(buf));	if (rc < 0)		goto ret;	msleep(100);	priv->frequency = freq;	tuner_dbg("divisor= %02x %02x %02x %02x (freq=%d.%03d)\n",	       buf[0], buf[1], buf[2], buf[3],	       freq / 1000000, (freq % 1000000) / 1000);	rc = 0;ret:	mutex_unlock(&priv->lock);	return rc;}static int xc2028_set_analog_freq(struct dvb_frontend *fe,			      struct analog_parameters *p){	struct xc2028_data *priv = fe->tuner_priv;	unsigned int       type=0;	tuner_dbg("%s called\n", __func__);	if (p->mode == V4L2_TUNER_RADIO) {		type |= FM;		if (priv->ctrl.input1)			type |= INPUT1;		return generic_set_freq(fe, (625l * p->frequency) / 10,				T_ANALOG_TV, type, 0, 0);	}	/* if std is not defined, choose one */	if (!p->std)		p->std = V4L2_STD_MN;	/* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */	if (!(p->std & V4L2_STD_MN))		type |= F8MHZ;	/* Add audio hack to std mask */	p->std |= parse_audio_std_option();	return generic_set_freq(fe, 62500l * p->frequency,				T_ANALOG_TV, type, p->std, 0);}static int xc2028_set_params(struct dvb_frontend *fe,			     struct dvb_frontend_parameters *p){	struct xc2028_data *priv = fe->tuner_priv;	unsigned int       type=0;	fe_bandwidth_t     bw = BANDWIDTH_8_MHZ;	u16                demod = 0;	tuner_dbg("%s called\n", __func__);	switch(fe->ops.info.type) {	case FE_OFDM:		bw = p->u.ofdm.bandwidth;		break;	case FE_QAM:		tuner_info("WARN: There are some reports that "			   "QAM 6 MHz doesn't work.\n"			   "If this works for you, please report by "			   "e-mail to: v4l-dvb-maintainer@linuxtv.org\n");		bw = BANDWIDTH_6_MHZ;		type |= QAM;		break;	case FE_ATSC:		bw = BANDWIDTH_6_MHZ;		/* The only ATSC firmware (at least on v2.7) is D2633 */		type |= ATSC | D2633;		break;	/* DVB-S is not supported */	default:		return -EINVAL;	}	switch (bw) {	case BANDWIDTH_8_MHZ:		if (p->frequency < 470000000)			priv->ctrl.vhfbw7 = 0;		else			priv->ctrl.uhfbw8 = 1;		type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8;		type |= F8MHZ;		break;	case BANDWIDTH_7_MHZ:		if (p->frequency < 470000000)			priv->ctrl.vhfbw7 = 1;		else			priv->ctrl.uhfbw8 = 0;		type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7;		type |= F8MHZ;		break;	case BANDWIDTH_6_MHZ:		type |= DTV6;		priv->ctrl.vhfbw7 = 0;		priv->ctrl.uhfbw8 = 0;		break;	default:		tuner_err("error: bandwidth not supported.\n");	};	/*	  Selects between D2633 or D2620 firmware.	  It doesn't make sense for ATSC, since it should be D2633 on all cases	 */	if (fe->ops.info.type != FE_ATSC) {		switch (priv->ctrl.type) {		case XC2028_D2633:			type |= D2633;			break;		case XC2028_D2620:			type |= D2620;			break;		case XC2028_AUTO:		default:			/* Zarlink seems to need D2633 */			if (priv->ctrl.demod == XC3028_FE_ZARLINK456)				type |= D2633;			else				type |= D2620;		}	}	/* All S-code tables need a 200kHz shift */	if (priv->ctrl.demod)		demod = priv->ctrl.demod + 200;	return generic_set_freq(fe, p->frequency,				T_DIGITAL_TV, type, 0, demod);}#if 0/* This is needed at sleep (S1/S3), but not at fe_standby. Otherwise,   firmware will be loaded on every open() */static int xc2028_sleep(struct dvb_frontend *fe){	struct xc2028_data *priv = fe->tuner_priv;	int rc = 0;	tuner_dbg("%s called\n", __func__);	mutex_lock(&priv->lock);	if (priv->firm_version < 0x0202)		rc = send_seq(priv, {0x00, 0x08, 0x00, 0x00});	else		rc = send_seq(priv, {0x80, 0x08, 0x00, 0x00});	priv->cur_fw.type = 0;	/* need firmware reload */	mutex_unlock(&priv->lock);	return rc;}#endifstatic int xc2028_dvb_release(struct dvb_frontend *fe){	struct xc2028_data *priv = fe->tuner_priv;	tuner_dbg("%s called\n", __func__);	mutex_lock(&xc2028_list_mutex);	/* only perform final cleanup if this is the last instance */	if (hybrid_tuner_report_instance_count(priv) == 1) {		kfree(priv->ctrl.fname);		free_firmware(priv);	}	if (priv)		hybrid_tuner_release_state(priv);	mutex_unlock(&xc2028_list_mutex);	fe->tuner_priv = NULL;	return 0;}static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency){	struct xc2028_data *priv = fe->tuner_priv;	tuner_dbg("%s called\n", __func__);	*frequency = priv->frequency;	return 0;}static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg){	struct xc2028_data *priv = fe->tuner_priv;	struct xc2028_ctrl *p    = priv_cfg;	int                 rc   = 0;	tuner_dbg("%s called\n", __func__);	mutex_lock(&priv->lock);	memcpy(&priv->ctrl, p, sizeof(priv->ctrl));	if (priv->ctrl.max_len < 9)		priv->ctrl.max_len = 13;	if (p->fname) {		if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) {			kfree(priv->ctrl.fname);			free_firmware(priv);		}		priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);		if (priv->ctrl.fname == NULL)			rc = -ENOMEM;	}	mutex_unlock(&priv->lock);	return rc;}static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {	.info = {		 .name = "Xceive XC3028",		 .frequency_min = 42000000,		 .frequency_max = 864000000,		 .frequency_step = 50000,		 },	.set_config	   = xc2028_set_config,	.set_analog_params = xc2028_set_analog_freq,	.release           = xc2028_dvb_release,	.get_frequency     = xc2028_get_frequency,	.get_rf_strength   = xc2028_signal,	.set_params        = xc2028_set_params,#if 0	.sleep             = xc2028_sleep,#endif#if 0	int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);	int (*get_status)(struct dvb_frontend *fe, u32 *status);#endif};struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,				   struct xc2028_config *cfg){	struct xc2028_data *priv;	int instance;	if (debug)		printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n");	if (NULL == cfg)		return NULL;	if (!fe) {		printk(KERN_ERR "xc2028: No frontend!\n");		return NULL;	}	mutex_lock(&xc2028_list_mutex);	instance = hybrid_tuner_request_state(struct xc2028_data, priv,					      hybrid_tuner_instance_list,					      cfg->i2c_adap, cfg->i2c_addr,					      "xc2028");	switch (instance) {	case 0:		/* memory allocation failure */		goto fail;		break;	case 1:		/* new tuner instance */		priv->ctrl.max_len = 13;		mutex_init(&priv->lock);		fe->tuner_priv = priv;		break;	case 2:		/* existing tuner instance */		fe->tuner_priv = priv;		break;	}	memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,	       sizeof(xc2028_dvb_tuner_ops));	tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");	if (cfg->ctrl)		xc2028_set_config(fe, cfg->ctrl);	mutex_unlock(&xc2028_list_mutex);	return fe;fail:	mutex_unlock(&xc2028_list_mutex);	xc2028_dvb_release(fe);	return NULL;}EXPORT_SYMBOL(xc2028_attach);MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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