Update: the following patch, which should be applied after the previous
ones, makes a few additional changes to the XC4000 driver:
  - adds support for DTV7
  - implements power management
  - adds a mutex and locking for tuner operations
  - some unused or unneeded code has been removed

On 02/09/2010 06:35 PM, istva...@mailbox.hu wrote:

> There are two separate patches for v4l-dvb revision 28f5eca12bb0: the
> first one adds the XC4000 driver, while the second one adds support for
> the Leadtek WinFast DTV2000H Plus card in the CX88 driver.
> 
> http://www.sharemation.com/IstvanV/v4l/xc4000-28f5eca12bb0.patch
> http://www.sharemation.com/IstvanV/v4l/cx88-dtv2000h+-28f5eca12bb0.patch
> 
> These new firmware files are more complete than the previous ones, but
> are not compatible with the original driver. Both version 1.2 and 1.4
> are available:
> 
> http://www.sharemation.com/IstvanV/v4l/xc4000-1.2.fw
> http://www.sharemation.com/IstvanV/v4l/xc4000-1.4.fw
> 
> The following simple utility was used for creating the firmware files.
> 
> http://www.sharemation.com/IstvanV/v4l/xc4000fw.c
diff -r -d -N -U4 v4l-dvb-28f5eca12bb0.old/linux/drivers/media/common/tuners/xc4000.c v4l-dvb-28f5eca12bb0/linux/drivers/media/common/tuners/xc4000.c
--- v4l-dvb-28f5eca12bb0.old/linux/drivers/media/common/tuners/xc4000.c	2010-02-11 20:08:39.000000000 +0100
+++ v4l-dvb-28f5eca12bb0/linux/drivers/media/common/tuners/xc4000.c	2010-02-11 20:05:01.000000000 +0100
@@ -27,8 +27,9 @@
 #include <linux/videodev2.h>
 #include <linux/delay.h>
 #include <linux/dvb/frontend.h>
 #include <linux/i2c.h>
+#include <linux/mutex.h>
 #include <asm/unaligned.h>
 
 #include "dvb_frontend.h"
 
@@ -61,9 +62,9 @@
 	"The valid values are a sum of:\n"
 	" 1: use NICAM/B and A2/B instead of NICAM/A and A2/A\n"
 	" 2: use A2 instead of NICAM or BTSC\n"
 	" 4: use SECAM/K3 instead of K1\n"
-	" 8: force SECAM-L audio\n"
+	" 8: use PAL-D/K audio for SECAM-D/K\n"
 	"16: use FM radio input 1 instead of input 2\n"
 	"32: use mono audio (the lower three bits are ignored)\n");
 
 #define XC4000_DEFAULT_FIRMWARE "xc4000.fw"
@@ -108,18 +109,20 @@
 	u32	bandwidth;
 	u8	video_standard;
 	u8	rf_mode;
 	u8	card_type;
+	u8	ignore_i2c_write_errors;
  /*	struct xc2028_ctrl	ctrl; */
 	struct firmware_properties cur_fw;
 	__u16	hwmodel;
 	__u16	hwvers;
-	u8	ignore_i2c_write_errors;
+	struct mutex	lock;
 };
 
 /* Misc Defines */
 #define MAX_TV_STANDARD			24
 #define XC_MAX_I2C_WRITE_LENGTH		64
+#define XC_POWERED_DOWN			0x80000000U
 
 /* Signal Types */
 #define XC_RF_MODE_AIR			0
 #define XC_RF_MODE_CABLE		1
@@ -240,9 +243,9 @@
 	{"D/K-PAL-MONO",	0x0078, 0x8049, 6500},
 	{"D/K-SECAM-A2 DK1",	0x0000, 0x8049, 6340},
 	{"D/K-SECAM-A2 L/DK3",	0x0000, 0x8049, 6000},
 	{"D/K-SECAM-A2 MONO",	0x0078, 0x8049, 6500},
-	{"D/K-SECAM-NICAM",	0x8080, 0x8049, 6200},
+	{"D/K-SECAM-NICAM",	0x0080, 0x8049, 6200},
 	{"L-SECAM-NICAM",	0x8080, 0x0009, 6200},
 	{"L'-SECAM-NICAM",	0x8080, 0x4009, 6200},
 	{"DTV6",		0x00C0, 0x8002,    0},
 	{"DTV8",		0x00C0, 0x800B,    0},
@@ -251,11 +254,8 @@
 	{"FM Radio-INPUT2",	0x0008, 0x9800,10700},
 	{"FM Radio-INPUT1",	0x0008, 0x9000,10700}
 };
 
-#if 0
-static int xc4000_is_firmware_loaded(struct dvb_frontend *fe);
-#endif
 static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val);
 static int xc4000_TunerReset(struct dvb_frontend *fe);
 
 static int xc_send_i2c_data(struct xc4000_priv *priv, u8 *buf, int len)
@@ -275,25 +275,8 @@
 	}
 	return XC_RESULT_SUCCESS;
 }
 
-/* This routine is never used because the only time we read data from the
-   i2c bus is when we read registers, and we want that to be an atomic i2c
-   transaction in case we are on a multi-master bus */
-#if 0
-static int xc_read_i2c_data(struct xc4000_priv *priv, u8 *buf, int len)
-{
-	struct i2c_msg msg = { .addr = priv->i2c_props.addr,
-		.flags = I2C_M_RD, .buf = buf, .len = len };
-
-	if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
-		printk(KERN_ERR "xc4000 I2C read failed (len=%i)\n", len);
-		return -EREMOTEIO;
-	}
-	return 0;
-}
-#endif
-
 static void xc_wait(int wait_ms)
 {
 	msleep(wait_ms);
 }
@@ -444,17 +427,8 @@
 	   only be used for fast scanning for channel lock) */
 	return xc_write_reg(priv, XREG_RF_FREQ, freq_code); /* WAS: XREG_FINERFREQ */
 }
 
-#if 0
-/* We'll probably need these for analog support */
-static int xc_set_Xtal_frequency(struct xc4000_priv *priv, u32 xtalFreqInKHz)
-{
-	u16 xtalRatio = (32000 * 0x8000)/xtalFreqInKHz;
-	return xc_write_reg(priv, XREG_XTALFREQ, xtalRatio);
-}
-#endif
-
 static int xc_get_ADC_Envelope(struct xc4000_priv *priv, u16 *adc_envelope)
 {
 	return xc4000_readreg(priv, XREG_ADC_ENV, adc_envelope);
 }
@@ -1134,8 +1108,11 @@
 	u16	quality;
 	u8	hw_majorversion = 0, hw_minorversion = 0;
 	u8	fw_majorversion = 0, fw_minorversion = 0;
 
+	if (!(priv->cur_fw.type & BASE))
+		return;
+
 	/* Wait for stats to stabilize.
 	 * Frame Lines needs two frame times after initial lock
 	 * before it is valid.
 	 */
@@ -1174,12 +1151,14 @@
 	struct dvb_frontend_parameters *params)
 {
 	struct xc4000_priv *priv = fe->tuner_priv;
 	unsigned int type;
-	int ret;
+	int	ret = -EREMOTEIO;
 
 	dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency);
 
+	mutex_lock(&priv->lock);
+
 	if (fe->ops.info.type == FE_ATSC) {
 		dprintk(1, "%s() ATSC\n", __func__);
 		switch (params->u.vsb.modulation) {
 		case VSB_8:
@@ -1201,9 +1180,10 @@
 			priv->video_standard = XC4000_DTV6;
 			type = DTV6;
 			break;
 		default:
-			return -EINVAL;
+			ret = -EINVAL;
+			goto fail;
 		}
 	} else if (fe->ops.info.type == FE_OFDM) {
 		dprintk(1, "%s() OFDM\n", __func__);
 		switch (params->u.ofdm.bandwidth) {
@@ -1213,44 +1193,56 @@
 			priv->freq_hz = params->frequency - 1750000;
 			type = DTV6;
 			break;
 		case BANDWIDTH_7_MHZ:
-			printk(KERN_ERR "xc4000 bandwidth 7MHz not supported\n");
+			priv->bandwidth = BANDWIDTH_7_MHZ;
+			priv->video_standard = XC4000_DTV7;
+			priv->freq_hz = params->frequency - 2250000;
 			type = DTV7;
-			return -EINVAL;
+			break;
 		case BANDWIDTH_8_MHZ:
 			priv->bandwidth = BANDWIDTH_8_MHZ;
 			priv->video_standard = XC4000_DTV8;
 			priv->freq_hz = params->frequency - 2750000;
 			type = DTV8;
 			break;
+		case BANDWIDTH_AUTO:
+			if (params->frequency < 400000000) {
+				priv->bandwidth = BANDWIDTH_7_MHZ;
+				priv->freq_hz = params->frequency - 2250000;
+			} else {
+				priv->bandwidth = BANDWIDTH_8_MHZ;
+				priv->freq_hz = params->frequency - 2750000;
+			}
+			priv->video_standard = XC4000_DTV7_8;
+			type = DTV78;
+			break;
 		default:
 			printk(KERN_ERR "xc4000 bandwidth not set!\n");
-			return -EINVAL;
+			ret = -EINVAL;
+			goto fail;
 		}
 		priv->rf_mode = XC_RF_MODE_AIR;
 	} else {
 		printk(KERN_ERR "xc4000 modulation type not supported!\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto fail;
 	}
 
 	dprintk(1, "%s() frequency=%d (compensated)\n",
 		__func__, priv->freq_hz);
 
 	/* Make sure the correct firmware type is loaded */
-	if (check_firmware(fe, type, 0, priv->if_khz) != XC_RESULT_SUCCESS) {
-		return -EREMOTEIO;
-	}
+	if (check_firmware(fe, type, 0, priv->if_khz) != XC_RESULT_SUCCESS)
+		goto fail;
 
 	ret = xc_SetSignalSource(priv, priv->rf_mode);
 	if (ret != XC_RESULT_SUCCESS) {
 		printk(KERN_ERR
-			"xc4000: xc_SetSignalSource(%d) failed\n",
-			priv->rf_mode);
-		return -EREMOTEIO;
-	}
-
-	{
+		       "xc4000: xc_SetSignalSource(%d) failed\n",
+		       priv->rf_mode);
+		goto fail;
+	} else {
 		u16	video_mode, audio_mode;
 		video_mode = XC4000_Standard[priv->video_standard].VideoMode;
 		audio_mode = XC4000_Standard[priv->video_standard].AudioMode;
 		if (type == DTV6 && priv->firm_version != 0x0102)
@@ -1258,79 +1250,52 @@
 		ret = xc_SetTVStandard(priv, video_mode, audio_mode);
 		if (ret != XC_RESULT_SUCCESS) {
 			printk(KERN_ERR "xc4000: xc_SetTVStandard failed\n");
 			/* DJH - do not return when it fails... */
-			/* return -EREMOTEIO; */
+			/* goto fail; */
 		}
 	}
 
 	if (priv->card_type == XC4000_CARD_DTV2000H_PLUS) {
-		ret = 0;
-		if (xc_write_reg(priv, XREG_D_CODE, 0) != 0)
-			ret = -EREMOTEIO;
+		if (xc_write_reg(priv, XREG_D_CODE, 0) == 0)
+			ret = 0;
 		if (xc_write_reg(priv, XREG_AMPLITUDE,
 				 (priv->firm_version == 0x0102 ? 132 : 134))
 		    != 0)
 			ret = -EREMOTEIO;
 		if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0)
 			ret = -EREMOTEIO;
 		if (ret != 0) {
 			printk(KERN_ERR "xc4000: setting registers failed\n");
-			/* return ret; */
+			/* goto fail; */
 		}
 	}
 
-#ifdef DJH_DEBUG
-	ret = xc_set_IF_frequency(priv, priv->if_khz);
-	if (ret != XC_RESULT_SUCCESS) {
-		printk(KERN_ERR "xc4000: xc_Set_IF_frequency(%d) failed\n",
-		       priv->if_khz);
-		return -EIO;
-	}
-#endif
 	xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL);
 
 	if (debug)
 		xc_debug_dump(priv);
+	ret = 0;
 
-	return 0;
-}
-
-#if 0
-static int xc4000_is_firmware_loaded(struct dvb_frontend *fe)
-{
-	struct xc4000_priv *priv = fe->tuner_priv;
-	int ret;
-	u16 id;
-
-	ret = xc4000_readreg(priv, XREG_PRODUCT_ID, &id);
-	if (ret == XC_RESULT_SUCCESS) {
-		if (id == XC_PRODUCT_ID_FW_NOT_LOADED)
-			ret = XC_RESULT_RESET_FAILURE;
-		else
-			ret = XC_RESULT_SUCCESS;
-	}
+fail:
+	mutex_unlock(&priv->lock);
 
-	dprintk(1, "%s() returns %s id = 0x%x\n", __func__,
-		ret == XC_RESULT_SUCCESS ? "True" : "False", id);
 	return ret;
 }
-#endif
 
 static int xc4000_set_analog_params(struct dvb_frontend *fe,
 	struct analog_parameters *params)
 {
 	struct xc4000_priv *priv = fe->tuner_priv;
 	unsigned int type = 0;
-	int	ret;
-
-	/* Fix me: it could be air. */
-	priv->rf_mode = XC_RF_MODE_CABLE;
+	int	ret = -EREMOTEIO;
 
 	if (params->mode == V4L2_TUNER_RADIO) {
 		dprintk(1, "%s() frequency=%d (in units of 62.5Hz)\n",
 			__func__, params->frequency);
 
+		mutex_lock(&priv->lock);
+
 		params->std = 0;
 		priv->freq_hz = params->frequency * 125L / 2;
 
 		if (audio_std & XC4000_AUDIO_STD_INPUT1) {
@@ -1346,8 +1311,10 @@
 
 	dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
 		__func__, params->frequency);
 
+	mutex_lock(&priv->lock);
+
 	/* params->frequency is in units of 62.5khz */
 	priv->freq_hz = params->frequency * 62500;
 
 	/* if std is not defined, choose one */
@@ -1454,24 +1421,24 @@
 		goto tune_channel;
 	}
 
 tune_channel:
+	/* Fix me: it could be air. */
+	priv->rf_mode = XC_RF_MODE_CABLE;
 
 	if (check_firmware(fe, type, params->std,
 			   XC4000_Standard[priv->video_standard].int_freq)
 	    != XC_RESULT_SUCCESS) {
-		return -EREMOTEIO;
+		goto fail;
 	}
 
 	ret = xc_SetSignalSource(priv, priv->rf_mode);
 	if (ret != XC_RESULT_SUCCESS) {
 		printk(KERN_ERR
 			"xc4000: xc_SetSignalSource(%d) failed\n",
 			priv->rf_mode);
-		return -EREMOTEIO;
-	}
-
-	{
+		goto fail;
+	} else {
 		u16	video_mode, audio_mode;
 		video_mode = XC4000_Standard[priv->video_standard].VideoMode;
 		audio_mode = XC4000_Standard[priv->video_standard].AudioMode;
 		if (priv->video_standard < XC4000_BG_PAL_A2) {
@@ -1486,49 +1453,58 @@
 		}
 		ret = xc_SetTVStandard(priv, video_mode, audio_mode);
 		if (ret != XC_RESULT_SUCCESS) {
 			printk(KERN_ERR "xc4000: xc_SetTVStandard failed\n");
-			return -EREMOTEIO;
+			goto fail;
 		}
 	}
 
 	if (priv->card_type == XC4000_CARD_DTV2000H_PLUS) {
-		ret = 0;
-		if (xc_write_reg(priv, XREG_D_CODE, 0) != 0)
-			ret = -EREMOTEIO;
+		if (xc_write_reg(priv, XREG_D_CODE, 0) == 0)
+			ret = 0;
 		if (xc_write_reg(priv, XREG_AMPLITUDE, 1) != 0)
 			ret = -EREMOTEIO;
 		if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0)
 			ret = -EREMOTEIO;
 		if (ret != 0) {
 			printk(KERN_ERR "xc4000: setting registers failed\n");
-			return ret;
+			goto fail;
 		}
 	}
 
 	xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
 
 	if (debug)
 		xc_debug_dump(priv);
+	ret = 0;
 
-	return 0;
+fail:
+	mutex_unlock(&priv->lock);
+
+	return ret;
 }
 
 static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq)
 {
 	struct xc4000_priv *priv = fe->tuner_priv;
 
 	*freq = priv->freq_hz;
 
-	if (debug && (priv->cur_fw.type
-		      & (BASE | FM | DTV6 | DTV7 | DTV78 | DTV8)) == BASE) {
-		u16	snr = 0;
-		if (xc4000_readreg(priv, XREG_SNR, &snr) == 0) {
-			dprintk(1, "%s() freq = %u, SNR = %d\n",
-				__func__, *freq, snr);
-			return 0;
+	if (debug) {
+		mutex_lock(&priv->lock);
+		if ((priv->cur_fw.type
+		     & (BASE | FM | DTV6 | DTV7 | DTV78 | DTV8)) == BASE) {
+			u16	snr = 0;
+			if (xc4000_readreg(priv, XREG_SNR, &snr) == 0) {
+				mutex_unlock(&priv->lock);
+				dprintk(1, "%s() freq = %u, SNR = %d\n",
+					__func__, *freq, snr);
+				return 0;
+			}
 		}
+		mutex_unlock(&priv->lock);
 	}
+
 	dprintk(1, "%s()\n", __func__);
 
 	return 0;
 }
@@ -1546,58 +1522,57 @@
 {
 	struct xc4000_priv *priv = fe->tuner_priv;
 	u16	lock_status = 0;
 
-	xc_get_lock_status(priv, &lock_status);
+	mutex_lock(&priv->lock);
 
-	dprintk(2, "%s() lock_status = %d\n", __func__, lock_status);
+	if (priv->cur_fw.type & BASE)
+		xc_get_lock_status(priv, &lock_status);
 
 	*status = (lock_status == 1 ?
 		   TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO : 0);
+	if (priv->cur_fw.type & (DTV6 | DTV7 | DTV78 | DTV8))
+		*status &= (~TUNER_STATUS_STEREO);
+
+	mutex_unlock(&priv->lock);
+
+	dprintk(2, "%s() lock_status = %d\n", __func__, lock_status);
 
 	return 0;
 }
 
 static int xc4000_sleep(struct dvb_frontend *fe)
 {
-	/* FIXME: djh disable this for now... */
-	return XC_RESULT_SUCCESS;
-#if 0
-	int ret;
+	struct xc4000_priv *priv = fe->tuner_priv;
+	int	ret = XC_RESULT_SUCCESS;
 
 	dprintk(1, "%s()\n", __func__);
 
+	mutex_lock(&priv->lock);
+
 	/* Avoid firmware reload on slow devices */
-	if (no_poweroff)
-		return 0;
+	if (!no_poweroff && priv->cur_fw.type != XC_POWERED_DOWN) {
+		/* force reset and firmware reload */
+		priv->cur_fw.type = XC_POWERED_DOWN;
 
-	/* According to Xceive technical support, the "powerdown" register
-	   was removed in newer versions of the firmware.  The "supported"
-	   way to sleep the tuner is to pull the reset pin low for 10ms */
-	ret = xc4000_TunerReset(fe);
-	if (ret != XC_RESULT_SUCCESS) {
-		printk(KERN_ERR
-			"xc4000: %s() unable to shutdown tuner\n",
-			__func__);
-		return -EREMOTEIO;
-	} else
-		return XC_RESULT_SUCCESS;
-#endif
+		if (xc_write_reg(priv, XREG_POWER_DOWN, 0)
+		    != XC_RESULT_SUCCESS) {
+			printk(KERN_ERR
+			       "xc4000: %s() unable to shutdown tuner\n",
+			       __func__);
+			ret = -EREMOTEIO;
+		}
+	}
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
 }
 
 static int xc4000_init(struct dvb_frontend *fe)
 {
-	struct xc4000_priv *priv = fe->tuner_priv;
 	dprintk(1, "%s()\n", __func__);
 
-	if (check_firmware(fe, DTV8, 0, priv->if_khz) != XC_RESULT_SUCCESS) {
-		printk(KERN_ERR "xc4000: Unable to initialise tuner\n");
-		return -EREMOTEIO;
-	}
-
-	if (debug)
-		xc_debug_dump(priv);
-
 	return 0;
 }
 
 static int xc4000_release(struct dvb_frontend *fe)
@@ -1641,10 +1616,10 @@
 				   struct i2c_adapter *i2c,
 				   struct xc4000_config *cfg)
 {
 	struct xc4000_priv *priv = NULL;
-	int instance;
-	u16 id = 0;
+	int	instance;
+	u16	id = 0;
 
 	if (cfg->card_type != XC4000_CARD_GENERIC) {
 		if (cfg->card_type == XC4000_CARD_DTV2000H_PLUS) {
 			cfg->i2c_address = 0x61;
@@ -1671,8 +1646,9 @@
 		break;
 	case 1:
 		/* new tuner instance */
 		priv->bandwidth = BANDWIDTH_6_MHZ;
+		mutex_init(&priv->lock);
 		fe->tuner_priv = priv;
 		break;
 	default:
 		/* existing tuner instance */
@@ -1690,10 +1666,17 @@
 	/* Check if firmware has been loaded. It is possible that another
 	   instance of the driver has loaded the firmware.
 	 */
 
-	if (xc4000_readreg(priv, XREG_PRODUCT_ID, &id) != XC_RESULT_SUCCESS)
+	if (instance == 1) {
+		if (xc4000_readreg(priv, XREG_PRODUCT_ID, &id)
+		    != XC_RESULT_SUCCESS)
 			goto fail;
+	}
+	else {
+		id = ((priv->cur_fw.type & BASE) == 0 ?
+		      XC_PRODUCT_ID_FW_NOT_LOADED : XC_PRODUCT_ID_FW_LOADED);
+	}
 
 	switch (id) {
 	case XC_PRODUCT_ID_FW_LOADED:
 		printk(KERN_INFO
@@ -1720,12 +1703,8 @@
 
 	memcpy(&fe->ops.tuner_ops, &xc4000_tuner_ops,
 		sizeof(struct dvb_tuner_ops));
 
-	/* FIXME: For now, load the firmware at startup.  We will remove this
-	   before the code goes to production... */
-	check_firmware(fe, DTV8, 0, priv->if_khz);
-
 	return fe;
 fail:
 	mutex_unlock(&xc4000_list_mutex);
 

Reply via email to