V4L/DVB (6430): Convert tuner-xc2028 driver to the newer hybrid approach

This changeset converts tuner-xc2028 to the newer hybrid approach. It also
prevents creating twice the xc3028 private struct by both DVB and V4L parts.

Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 1604f04..dfb5259 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -69,6 +69,7 @@
 config VIDEO_TUNER
 	tristate
 	depends on I2C
+	select TUNER_XC2028 if !VIDEO_TUNER_CUSTOMIZE
 	select TUNER_MT20XX if !VIDEO_TUNER_CUSTOMIZE
 	select TUNER_TDA8290 if !VIDEO_TUNER_CUSTOMIZE
 	select TUNER_TEA5761 if !VIDEO_TUNER_CUSTOMIZE
@@ -89,6 +90,13 @@
 
 if VIDEO_TUNER_CUSTOMIZE
 
+config TUNER_XC2028
+	tristate "XCeive xc2028/xc3028 tuners"
+	depends on I2C
+	default m if VIDEO_TUNER_CUSTOMIZE
+	help
+	  Say Y here to include support for the xc2028/xc3028 tuners.
+
 config TUNER_MT20XX
 	tristate "Microtune 2032 / 2050 tuners"
 	depends on I2C
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 4548463..ea5be37 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -505,18 +505,6 @@
 	  Say Y here to include support for Philips SAB3036 compatible tuners.
 	  If in doubt, say N.
 
-config TUNER_XC2028
-	tristate "Xceive xc2028 support for tm5600/tm6000 driver"
-	depends on I2C
-	select VIDEO_TUNER
-	help
-	  Say Y here to include support for Xceive xc2028 tuner. This is
-	  required on a few tm5600/tm6000 designs. You should notice
-	  that this module currently works only with the special
-	  firmware versions used on those Trident chips.
-
-	  If in doubt, say N.
-
 config VIDEO_VINO
 	tristate "SGI Vino Video For Linux (EXPERIMENTAL)"
 	depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 1fd775e..08ac197 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -6,10 +6,6 @@
 			zoran_driver.o zoran_card.o
 tuner-objs	:=	tuner-core.o tuner-types.o tda9887.o
 
-ifneq ($(CONFIG_TUNER_XC2028),)
-  tuner-objs	+=	tuner-xc2028.o
-endif
-
 msp3400-objs	:=	msp3400-driver.o msp3400-kthreads.o
 
 obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o \
@@ -85,6 +81,7 @@
 
 obj-$(CONFIG_VIDEO_TUNER) += tuner.o
 
+obj-$(CONFIG_TUNER_XC2028) += tuner-xc2028.o
 obj-$(CONFIG_TUNER_SIMPLE) += tuner-simple.o
 obj-$(CONFIG_TUNER_MT20XX) += mt20xx.o
 obj-$(CONFIG_TUNER_TDA8290) += tda8290.o
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index ce817a1..1311273 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -24,6 +24,7 @@
 #include "tda8290.h"
 #include "tea5761.h"
 #include "tea5767.h"
+#include "tuner-xc2028.h"
 #include "tuner-simple.h"
 
 #define UNSET (-1U)
@@ -323,8 +324,17 @@
 		attach_simple_tuner(t);
 		break;
 	case TUNER_XC2028:
-		xc2028_tuner_init(c);
+	{
+		int rc=xc2028_attach(&t->fe, t->i2c.adapter, t->i2c.addr,
+				     &c->dev, c->adapter->algo_data,
+				     t->tuner_callback);
+		if (rc<0) {
+			t->type = TUNER_ABSENT;
+			t->mode_mask = T_UNINITIALIZED;
+			return;
+		}
 		break;
+	}
 	case TUNER_TDA9887:
 		tda9887_tuner_init(t);
 		break;
diff --git a/drivers/media/video/tuner-driver.h b/drivers/media/video/tuner-driver.h
index 5e733bc..28a10da 100644
--- a/drivers/media/video/tuner-driver.h
+++ b/drivers/media/video/tuner-driver.h
@@ -70,8 +70,6 @@
 	struct tuner_operations ops;
 };
 
-extern int xc2028_tuner_init(struct i2c_client *c);
-
 /* ------------------------------------------------------------------------ */
 
 extern int tda9887_tuner_init(struct tuner *t);
diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c
index 0e68002..e4c3718 100644
--- a/drivers/media/video/tuner-xc2028.c
+++ b/drivers/media/video/tuner-xc2028.c
@@ -13,16 +13,15 @@
 #include <linux/delay.h>
 #include <media/tuner.h>
 #include <linux/mutex.h>
-#include "tuner-driver.h"
+#include "tuner-i2c.h"
 #include "tuner-xc2028.h"
 
 #include <linux/dvb/frontend.h>
 #include "dvb_frontend.h"
 
-/* digital TV standards */
-#define V4L2_STD_DTV_6MHZ       ((v4l2_std_id)0x04000000)
-#define V4L2_STD_DTV_7MHZ       ((v4l2_std_id)0x08000000)
-#define V4L2_STD_DTV_8MHZ       ((v4l2_std_id)0x10000000)
+#define PREFIX "xc2028 "
+
+static LIST_HEAD(xc2028_list);
 
 /* Firmwares used on tm5600/tm6000 + xc2028/xc3028 */
 
@@ -40,6 +39,15 @@
 static const char *firmware_MN         = "tm_xc3028_MN_BTSC.fw";
 
 struct xc2028_data {
+	struct list_head        xc2028_list;
+	struct tuner_i2c_props  i2c_props;
+	int                     (*tuner_callback) (void *dev,
+						   int command, int arg);
+	struct device           *dev;
+	void			*video_dev;
+	int			count;
+	u32			frequency;
+
 	v4l2_std_id		firm_type;	   /* video stds supported
 							by current firmware */
 	fe_bandwidth_t		bandwidth;	   /* Firmware bandwidth:
@@ -48,61 +56,64 @@
 							      were loaded? */
 	enum tuner_mode	mode;
 	struct i2c_client	*i2c_client;
-	
-        struct mutex lock;
+
+	struct mutex lock;
 };
 
-#define i2c_send(rc,c,buf,size)						\
-if (size != (rc = i2c_master_send(c, buf, size)))			\
-	tuner_warn("i2c output error: rc = %d (should be %d)\n",	\
+#define i2c_send(rc, priv, buf, size)					\
+if (size != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size)))	\
+	tuner_info("i2c output error: rc = %d (should be %d)\n",	\
 	rc, (int)size);
 
-#define i2c_rcv(rc,c,buf,size)						\
-if (size != (rc = i2c_master_recv(c, buf, size)))			\
-	tuner_warn("i2c input error: rc = %d (should be %d)\n",		\
+#define i2c_rcv(rc, priv, buf, size)					\
+if (size != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size)))	\
+	tuner_info("i2c input error: rc = %d (should be %d)\n",		\
 	rc, (int)size);
 
-#define send_seq(c, data...)						\
+#define send_seq(priv, data...)						\
 {	int rc;								\
-	const static u8 _val[] = data;					\
+	static u8 _val[] = data;					\
 	if (sizeof(_val) !=						\
-				(rc = i2c_master_send 			\
-				(c, _val, sizeof(_val)))) {		\
-		printk(KERN_ERR "Error on line %d: %d\n",__LINE__,rc);	\
-		return;							\
+			(rc = tuner_i2c_xfer_send (&priv->i2c_props,	\
+						_val, sizeof(_val)))) {	\
+		tuner_info("Error on line %d: %d\n",__LINE__,rc);	\
+		return -EINVAL;							\
 	}								\
 	msleep (10);							\
 }
 
-static int xc2028_get_reg(struct i2c_client *c, u16 reg)
+static int xc2028_get_reg(struct xc2028_data *priv, u16 reg)
 {
 	int rc;
 	unsigned char buf[1];
-	struct tuner *t = i2c_get_clientdata(c);
+
+	tuner_info("%s called\n", __FUNCTION__);
 
 	buf[0]= reg;
 
-	i2c_send(rc, c, buf, sizeof(buf));
+	i2c_send(rc, priv, buf, sizeof(buf));
 	if (rc<0)
 		return rc;
 
-	i2c_rcv(rc, c, buf, 2);
+	i2c_rcv(rc, priv, buf, 2);
 	if (rc<0)
 		return rc;
 
 	return (buf[1])|(buf[0]<<8);
 }
 
-static int load_firmware (struct i2c_client *c, const char *name)
+static int load_firmware (struct dvb_frontend *fe, const char *name)
 {
+	struct xc2028_data      *priv = fe->tuner_priv;
 	const struct firmware *fw=NULL;
-	struct tuner          *t = i2c_get_clientdata(c);
 	unsigned char         *p, *endp;
 	int                   len=0, rc=0;
 	static const char     firmware_ver[] = "tm6000/xcv v1";
 
-	tuner_info("xc2028: Loading firmware %s\n", name);
-	rc = request_firmware(&fw, name, &c->dev);
+	tuner_info("%s called\n", __FUNCTION__);
+
+	tuner_info("Loading firmware %s\n", name);
+	rc = request_firmware(&fw, name, priv->dev);
 	if (rc < 0) {
 		if (rc==-ENOENT)
 			tuner_info("Error: firmware %s not found.\n", name);
@@ -138,7 +149,7 @@
 	while(p<endp) {
 		if ((*p) & 0x80) {
 			/* Special callback command received */
-			rc = t->tuner_callback(c->adapter->algo_data,
+			rc = priv->tuner_callback(priv->video_dev,
 					     XC2028_TUNER_RESET, (*p)&0x7f);
 			if (rc<0) {
 				tuner_info("Error at RESET code %d\n",
@@ -162,7 +173,7 @@
 			goto err;
 		}
 
-		i2c_send(rc, c, p, len);
+		i2c_send(rc, priv, p, len);
 		if (rc<0)
 			goto err;
 		p+=len;
@@ -179,171 +190,173 @@
 	return rc;
 }
 
-static int check_firmware(struct i2c_client *c, enum tuner_mode new_mode,
+static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
+						v4l2_std_id std,
 						fe_bandwidth_t bandwidth)
 {
+	struct xc2028_data      *priv = fe->tuner_priv;
 	int			rc, version;
-	struct tuner		*t = i2c_get_clientdata(c);
-	struct xc2028_data	*xc2028 = t->priv;
 	const char		*name;
 	int change_digital_bandwidth;
 
-	if (!t->tuner_callback) {
-		printk(KERN_ERR "xc2028: need tuner_callback to load firmware\n");
-		return -EINVAL;
-	}
+	tuner_info("%s called\n", __FUNCTION__);
 
-	printk(KERN_INFO "xc2028: I am in mode %u and I should switch to mode %i\n",
-							    xc2028->mode, new_mode);
+	tuner_info( "I am in mode %u and I should switch to mode %i\n",
+						    priv->mode, new_mode);
 
 	/* first of all, determine whether we have switched the mode */
-	if(new_mode != xc2028->mode) {
-		xc2028->mode = new_mode;
-		xc2028->need_load_generic = 1;
+	if(new_mode != priv->mode) {
+		priv->mode = new_mode;
+		priv->need_load_generic = 1;
 	}
 
-	change_digital_bandwidth = (xc2028->mode == T_DIGITAL_TV
-				 && bandwidth != xc2028->bandwidth) ? 1 : 0;
-	tuner_info("xc2028: old bandwidth %u, new bandwidth %u\n", xc2028->bandwidth,
+	change_digital_bandwidth = (priv->mode == T_DIGITAL_TV
+				 && bandwidth != priv->bandwidth) ? 1 : 0;
+	tuner_info("old bandwidth %u, new bandwidth %u\n", priv->bandwidth,
 								   bandwidth);
 
-	if (xc2028->need_load_generic) {
-		if (xc2028->bandwidth==8)
+	if (priv->need_load_generic) {
+		if (priv->bandwidth==8)
 			name = firmware_8MHZ_INIT0;
 		else
 			name = firmware_INIT0;
 
 		/* Reset is needed before loading firmware */
-		rc = t->tuner_callback(c->adapter->algo_data,
-				     XC2028_TUNER_RESET, 0);
+		rc = priv->tuner_callback(priv->video_dev,
+					  XC2028_TUNER_RESET, 0);
 		if (rc<0)
 			return rc;
 
-		rc = load_firmware(c,name);
+		rc = load_firmware(fe,name);
 		if (rc<0)
 			return rc;
 
-		xc2028->need_load_generic=0;
-		xc2028->firm_type=0;
-		if(xc2028->mode == T_DIGITAL_TV) {
+		priv->need_load_generic=0;
+		priv->firm_type=0;
+		if(priv->mode == T_DIGITAL_TV) {
 			change_digital_bandwidth=1;
 		}
 	}
 
-	tuner_info("xc2028: I should change bandwidth %u\n",
+	tuner_info("I should change bandwidth %u\n",
 						   change_digital_bandwidth);
 
+	/* FIXME: t->std makes no sense here */
 	if (change_digital_bandwidth) {
 		switch(bandwidth) {
 			case BANDWIDTH_8_MHZ:
-				t->std = V4L2_STD_DTV_8MHZ;
+				std = V4L2_STD_DTV_8MHZ;
 			break;
 
 			case BANDWIDTH_7_MHZ:
-				t->std = V4L2_STD_DTV_7MHZ;
+				std = V4L2_STD_DTV_7MHZ;
 			break;
 
 			case BANDWIDTH_6_MHZ:
-				t->std = V4L2_STD_DTV_6MHZ;
+				std = V4L2_STD_DTV_6MHZ;
 			break;
 
 			default:
 				tuner_info("error: bandwidth not supported.\n");
 		};
-		xc2028->bandwidth = bandwidth;
+		priv->bandwidth = bandwidth;
 	}
 
-	if (xc2028->firm_type & t->std) {
+	if (priv->firm_type & std) {
 		tuner_info("xc3028: no need to load a std-specific firmware.\n");
 		return 0;
 	}
 
-	rc = load_firmware(c,firmware_INIT1);
+	rc = load_firmware(fe,firmware_INIT1);
 
-	if (t->std & V4L2_STD_MN)
+	if (std & V4L2_STD_MN)
 		name=firmware_MN;
-	else if (t->std & V4L2_STD_DTV_6MHZ)
+	else if (std & V4L2_STD_DTV_6MHZ)
 		name=firmware_6M;
-	else if (t->std & V4L2_STD_DTV_7MHZ)
+	else if (std & V4L2_STD_DTV_7MHZ)
 		name=firmware_7M;
-	else if (t->std & V4L2_STD_DTV_8MHZ)
+	else if (std & V4L2_STD_DTV_8MHZ)
 		name=firmware_8M;
-	else if (t->std & V4L2_STD_PAL_B)
+	else if (std & V4L2_STD_PAL_B)
 		name=firmware_B;
 	else
 		name=firmware_DK;
 
-	tuner_info("xc2028: loading firmware named %s.\n", name);
-	rc = load_firmware(c, name);
+	tuner_info("loading firmware named %s.\n", name);
+	rc = load_firmware(fe, name);
 	if (rc<0)
 		return rc;
 
-	version = xc2028_get_reg(c, 0x4);
+	version = xc2028_get_reg(priv, 0x4);
 	tuner_info("Firmware version is %d.%d\n",
 					(version>>4)&0x0f,(version)&0x0f);
 
-	xc2028->firm_type=t->std;
+	priv->firm_type=std;
 
 	return 0;
 }
 
-static int xc2028_signal(struct i2c_client *c)
+static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
 {
-	struct tuner       *t = i2c_get_clientdata(c);
-	struct xc2028_data *xc2028 = t->priv;
+	struct xc2028_data *priv = fe->tuner_priv;
 	int                frq_lock, signal=0;
 
-        mutex_lock(&xc2028->lock);
+	tuner_info("%s called\n", __FUNCTION__);
 
-	printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__);
+	mutex_lock(&priv->lock);
 
-	frq_lock = xc2028_get_reg(c, 0x2);
+	*strength = 0;
+
+	frq_lock = xc2028_get_reg(priv, 0x2);
 	if (frq_lock<=0)
 		goto ret;
 
 	/* Frequency is locked. Return signal quality */
 
-	signal = xc2028_get_reg(c, 0x40);
+	signal = xc2028_get_reg(priv, 0x40);
 
 	if(signal<=0) {
 		signal=frq_lock;
 	}
 
 ret:
-        mutex_unlock(&xc2028->lock);
+	mutex_unlock(&priv->lock);
 
-	return signal;
+	*strength = signal;
+
+	return 0;
 }
 
 #define DIV 15625
 
-static void generic_set_tv_freq(struct i2c_client *c, u32 freq /* in Hz */,
-				enum tuner_mode new_mode, fe_bandwidth_t bandwidth)
+static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */,
+				enum tuner_mode new_mode,
+				v4l2_std_id std,
+				fe_bandwidth_t bandwidth)
 {
-	int           rc;
+	struct xc2028_data *priv = fe->tuner_priv;
+	int           rc=-EINVAL;
 	unsigned char buf[5];
-	struct tuner  *t  = i2c_get_clientdata(c);
 	u32 div, offset = 0;
 
+	tuner_info("%s called\n", __FUNCTION__);
+
 	/* HACK: It seems that specific firmware need to be reloaded
 	   when freq is changed */
-	struct xc2028_data	*xc2028 = t->priv;
 
-        mutex_lock(&xc2028->lock);
+	mutex_lock(&priv->lock);
 
-	xc2028->firm_type=0;
+	priv->firm_type=0;
 
 	/* Reset GPIO 1 */
-	if (t->tuner_callback) {
-		rc = t->tuner_callback( c->adapter->algo_data,
-					XC2028_TUNER_RESET, 0);
-		if (rc<0)
-			goto ret;
-	}
-	msleep(10);
-	printk("xc3028: should set frequency %d kHz)\n", freq / 1000);
+	rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, 0);
+	if (rc<0)
+		goto ret;
 
-	if (check_firmware(c, new_mode, bandwidth)<0)
+	msleep(10);
+	tuner_info("should set frequency %d kHz)\n", freq / 1000);
+
+	if (check_firmware(fe, new_mode, std, bandwidth)<0)
 		goto ret;
 
 	if(new_mode == T_DIGITAL_TV)
@@ -352,13 +365,10 @@
 	div = (freq - offset + DIV/2)/DIV;
 
 	/* CMD= Set frequency */
-	send_seq(c, {0x00, 0x02, 0x00, 0x00});
-	if (t->tuner_callback) {
-		rc = t->tuner_callback( c->adapter->algo_data,
-					XC2028_RESET_CLK, 1);
-		if (rc<0)
-			goto ret;
-	}
+	send_seq(priv, {0x00, 0x02, 0x00, 0x00});
+	rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
+	if (rc<0)
+		goto ret;
 
 	msleep(10);
 
@@ -368,121 +378,82 @@
 	buf[3]= 0xff & (div);
 	buf[4]= 0;
 
-	i2c_send(rc, c, buf, sizeof(buf));
+	i2c_send(rc, priv, buf, sizeof(buf));
 	if (rc<0)
 		goto ret;
 	msleep(100);
 
+	priv->frequency=freq;
+
 	printk("divider= %02x %02x %02x %02x (freq=%d.%02d)\n",
 		 buf[1],buf[2],buf[3],buf[4],
-		 freq / 16, freq % 16 * 100 / 16);
+		 freq / 1000000, (freq%1000000)/10000);
+
+	rc=0;
 
 ret:
-        mutex_unlock(&xc2028->lock);
+	mutex_unlock(&priv->lock);
+
+	return rc;
 }
 
-
-static void set_tv_freq(struct i2c_client *c, unsigned int freq)
+static int xc2028_set_tv_freq(struct dvb_frontend *fe,
+			struct analog_parameters *p)
 {
-	printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__);
+	struct xc2028_data *priv = fe->tuner_priv;
 
-	generic_set_tv_freq(c, freq * 62500l, T_ANALOG_TV,
-					      BANDWIDTH_8_MHZ /* unimportant */);
+	tuner_info("%s called\n", __FUNCTION__);
+
+	return generic_set_tv_freq(fe, 62500l*p->frequency, T_ANALOG_TV,
+					      p->std,
+					      BANDWIDTH_8_MHZ /* NOT USED */);
 }
 
-static void xc2028_release(struct i2c_client *c)
-{
-	struct tuner *t = i2c_get_clientdata(c);
-
-	kfree(t->priv);
-	t->priv = NULL;
-}
-
-static struct tuner_operations tea5767_tuner_ops = {
-	.set_tv_freq    = set_tv_freq,
-	.has_signal     = xc2028_signal,
-	.release        = xc2028_release,
-//	.is_stereo      = xc2028_stereo,
-};
-
-
-static int init=0;
-
-int xc2028_tuner_init(struct i2c_client *c)
-{
-	struct tuner *t = i2c_get_clientdata(c);
-	int version = xc2028_get_reg(c, 0x4);
-	int prd_id = xc2028_get_reg(c, 0x8);
-	struct xc2028_data *xc2028;
-
-	tuner_info("Xcv2028/3028 init called!\n");
-
-	if (init) {
-		printk (KERN_ERR "Module already initialized!\n");
-		return 0;
-	}
-	init++;
-
-	xc2028 = kzalloc(sizeof(*xc2028), GFP_KERNEL);
-	if (!xc2028)
-		return -ENOMEM;
-	t->priv = xc2028;
-
-	xc2028->bandwidth=BANDWIDTH_6_MHZ;
-	xc2028->need_load_generic=1;
-	xc2028->mode = T_UNINITIALIZED;
-
-	mutex_init(&xc2028->lock);
-
-	/* FIXME: Check where t->priv will be freed */
-
-	if (version<0)
-		version=0;
-
-	if (prd_id<0)
-		prd_id=0;
-
-	strlcpy(c->name, "xc2028", sizeof(c->name));
-	tuner_info("type set to %d (%s, hw ver=%d.%d, fw ver=%d.%d, id=0x%04x)\n",
-		   t->type, c->name,
-		   (version>>12)&0x0f,(version>>8)&0x0f,
-		   (version>>4)&0x0f,(version)&0x0f, prd_id);
-
-	memcpy(&t->ops, &tea5767_tuner_ops, sizeof(struct tuner_operations));
-
-	return 0;
-}
-
-static int xc3028_set_params(struct dvb_frontend *fe,
+static int xc2028_set_params(struct dvb_frontend *fe,
 			     struct dvb_frontend_parameters *p)
 {
-	struct i2c_client *c = fe->tuner_priv;
+	struct xc2028_data *priv = fe->tuner_priv;
 
-	printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__);
+	tuner_info("%s called\n", __FUNCTION__);
 
-	generic_set_tv_freq(c, p->frequency, T_DIGITAL_TV,
-					     p->u.ofdm.bandwidth);
+	/* FIXME: Only OFDM implemented */
+	if (fe->ops.info.type != FE_OFDM) {
+		tuner_info ("DTV type not implemented.\n");
+		return -EINVAL;
+	}
 
-	return 0;
+	return generic_set_tv_freq(fe, p->frequency, T_DIGITAL_TV,
+						0, /* NOT USED */
+						p->u.ofdm.bandwidth);
+
 }
 
-static int xc3028_dvb_release(struct dvb_frontend *fe)
+static int xc2028_dvb_release(struct dvb_frontend *fe)
 {
-	printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__);
+	struct xc2028_data *priv = fe->tuner_priv;
 
-	fe->tuner_priv = NULL;
+	tuner_info("%s called\n", __FUNCTION__);
+
+	priv->count--;
+
+	if (!priv->count)
+		kfree (priv);
 
 	return 0;
 }
 
-static int xc3028_dvb_init(struct dvb_frontend *fe)
+static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 {
-	printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__);
+	struct xc2028_data *priv = fe->tuner_priv;
+
+	tuner_info("%s called\n", __FUNCTION__);
+
+	*frequency = priv->frequency;
 
 	return 0;
 }
 
-static const struct dvb_tuner_ops xc3028_dvb_tuner_ops = {
+static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
 	.info = {
 			.name           = "Xceive XC3028",
 			.frequency_min  =  42000000,
@@ -490,33 +461,74 @@
 			.frequency_step =     50000,
 		},
 
-	.release = xc3028_dvb_release,
-	.init = xc3028_dvb_init,
+	.set_analog_params = xc2028_set_tv_freq,
+	.release           = xc2028_dvb_release,
+	.get_frequency     = xc2028_get_frequency,
+	.get_rf_strength   = xc2028_signal,
+	.set_params        = xc2028_set_params,
 
 // 	int (*sleep)(struct dvb_frontend *fe);
-
-	/** This is for simple PLLs - set all parameters in one go. */
-	.set_params = xc3028_set_params,
-
-	/** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */
-// 	int (*calc_regs)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p, u8 *buf, int buf_len);
-
-// 	int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency);
 // 	int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
-
 // 	int (*get_status)(struct dvb_frontend *fe, u32 *status);
 };
 
-int xc2028_attach(struct i2c_client *c, struct dvb_frontend *fe)
+int xc2028_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap,
+		  u8 i2c_addr, struct device *dev, void *video_dev,
+		  int (*tuner_callback) (void *dev, int command,int arg))
 {
-	fe->tuner_priv = c;
+	struct xc2028_data *priv;
 
-	memcpy(&fe->ops.tuner_ops, &xc3028_dvb_tuner_ops, sizeof(fe->ops.tuner_ops));
+	printk( KERN_INFO PREFIX "Xcv2028/3028 init called!\n");
+
+	if (NULL == dev)
+		return -ENODEV;
+
+	if (NULL == video_dev)
+		return -ENODEV;
+
+	if (!tuner_callback) {
+		printk( KERN_ERR PREFIX "No tuner callback!\n");
+		return -EINVAL;
+	}
+
+	list_for_each_entry(priv, &xc2028_list, xc2028_list) {
+		if (priv->dev == dev) {
+			dev = NULL;
+			priv->count++;
+		}
+	}
+
+	if (dev) {
+		priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+		if (priv == NULL)
+			return -ENOMEM;
+
+		fe->tuner_priv = priv;
+
+		priv->bandwidth=BANDWIDTH_6_MHZ;
+		priv->need_load_generic=1;
+		priv->mode = T_UNINITIALIZED;
+		priv->i2c_props.addr = i2c_addr;
+		priv->i2c_props.adap = i2c_adap;
+		priv->dev = dev;
+		priv->video_dev = video_dev;
+		priv->tuner_callback = tuner_callback;
+
+		mutex_init(&priv->lock);
+
+		list_add_tail(&priv->xc2028_list,&xc2028_list);
+	}
+
+	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");
 
 	return 0;
 }
 
-
-
 EXPORT_SYMBOL(xc2028_attach);
 
+MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tuner-xc2028.h b/drivers/media/video/tuner-xc2028.h
index 34ff4cb..d5a18a3 100644
--- a/drivers/media/video/tuner-xc2028.h
+++ b/drivers/media/video/tuner-xc2028.h
@@ -4,6 +4,11 @@
  * This code is placed under the terms of the GNU General Public License v2
  */
 
+#ifndef __TUNER_XC2028_H__
+#define __TUNER_XC2028_H__
+
+#include "dvb_frontend.h"
+
 /* xc2028 commands for callback */
 #define XC2028_TUNER_RESET	0
 #define XC2028_RESET_CLK	1
@@ -11,4 +16,21 @@
 struct dvb_frontend;
 struct i2c_client;
 
-int xc2028_attach(struct i2c_client *c, struct dvb_frontend *fe);
+#if defined(CONFIG_TUNER_XC2028) || (defined(CONFIG_TUNER_XC2028_MODULE) && defined(MODULE))
+int xc2028_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap,
+		  u8 i2c_addr, struct device *dev, void *video_dev,
+		  int (*tuner_callback) (void *dev, int command,int arg));
+
+#else
+static inline int xc2028_attach(struct dvb_frontend *fe,
+		  struct i2c_adapter* i2c_adap,
+		  u8 i2c_addr, struct device *dev, void *video_dev,
+		  int (*tuner_callback) (void *dev, int command,int arg))
+{
+	printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
+	       __FUNCTION__);
+	return -EINVAL;
+}
+#endif
+
+#endif /* __TUNER_XC2028_H__ */