V4L/DVB (6430): Convert tuner-xc2028 driver to the newer hybrid approach
authorMauro Carvalho Chehab <mchehab@infradead.org>
Tue, 23 Oct 2007 18:24:06 +0000 (15:24 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Fri, 25 Jan 2008 21:01:05 +0000 (19:01 -0200)
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>
drivers/media/Kconfig
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/tuner-core.c
drivers/media/video/tuner-driver.h
drivers/media/video/tuner-xc2028.c
drivers/media/video/tuner-xc2028.h

index 1604f049040494dff638038665dd61c1de9586f3..dfb52592451fb67939355cd9dc4ef46c968a50d2 100644 (file)
@@ -69,6 +69,7 @@ source "drivers/media/common/Kconfig"
 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 @@ menuconfig VIDEO_TUNER_CUSTOMIZE
 
 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
index 454846355e9367b2691509078a2aa89443ba048e..ea5be3711a7d04f3bb29ace39ee70ce6cdfa49cd 100644 (file)
@@ -505,18 +505,6 @@ config TUNER_3036
          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
index 1fd775e0a0b05052aa4e932b86214605903c7f62..08ac197cc1dd8f5378ee82ed1f7ae7dc3353b20a 100644 (file)
@@ -6,10 +6,6 @@ zr36067-objs   :=      zoran_procfs.o zoran_device.o \
                        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_TUNER_3036) += tuner-3036.o
 
 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
index ce817a17ccf1f25d7d5a9285522fadc0d74e4cbc..13112732ed2ce1b8c949f0e4b874b9e9d3231b98 100644 (file)
@@ -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 @@ static void set_type(struct i2c_client *c, unsigned int type,
                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;
index 5e733bc8362d3d46b43d401ff8deec401d358a38..28a10da76d12958b790973cf51e0e2592d9f85bf 100644 (file)
@@ -70,8 +70,6 @@ struct tuner {
        struct tuner_operations ops;
 };
 
-extern int xc2028_tuner_init(struct i2c_client *c);
-
 /* ------------------------------------------------------------------------ */
 
 extern int tda9887_tuner_init(struct tuner *t);
index 0e68002a4a0467ad1e4f5abd9f5c1b686be130c2..e4c371896de4b7bc9eb1a320ef61d36a780e5a96 100644 (file)
 #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_DK         = "tm_xc3028_DK_PAL_MTS.fw";
 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 @@ struct xc2028_data {
                                                              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 @@ static int load_firmware (struct i2c_client *c, const char *name)
        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 @@ static int load_firmware (struct i2c_client *c, const char *name)
                        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 @@ err:
        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);
+
+       *strength = signal;
 
-       return 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;
-       }
+       rc = priv->tuner_callback(priv->video_dev, XC2028_TUNER_RESET, 0);
+       if (rc<0)
+               goto ret;
+
        msleep(10);
-       printk("xc3028: should set frequency %d kHz)\n", freq / 1000);
+       tuner_info("should set frequency %d kHz)\n", freq / 1000);
 
-       if (check_firmware(c, new_mode, bandwidth)<0)
+       if (check_firmware(fe, new_mode, std, bandwidth)<0)
                goto ret;
 
        if(new_mode == T_DIGITAL_TV)
@@ -352,13 +365,10 @@ static void generic_set_tv_freq(struct i2c_client *c, u32 freq /* in Hz */,
        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 @@ static void generic_set_tv_freq(struct i2c_client *c, u32 freq /* in Hz */,
        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);
 
-ret:
-        mutex_unlock(&xc2028->lock);
-}
+       rc=0;
 
+ret:
+       mutex_unlock(&priv->lock);
 
-static void set_tv_freq(struct i2c_client *c, unsigned int freq)
-{
-       printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__);
-
-       generic_set_tv_freq(c, freq * 62500l, T_ANALOG_TV,
-                                             BANDWIDTH_8_MHZ /* unimportant */);
+       return rc;
 }
 
-static void xc2028_release(struct i2c_client *c)
+static int xc2028_set_tv_freq(struct dvb_frontend *fe,
+                       struct analog_parameters *p)
 {
-       struct tuner *t = i2c_get_clientdata(c);
+       struct xc2028_data *priv = fe->tuner_priv;
 
-       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,
-};
+       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 int init=0;
-
-int xc2028_tuner_init(struct i2c_client *c)
+static int xc2028_set_params(struct dvb_frontend *fe,
+                            struct dvb_frontend_parameters *p)
 {
-       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;
+       struct xc2028_data *priv = fe->tuner_priv;
 
-       tuner_info("Xcv2028/3028 init called!\n");
+       tuner_info("%s called\n", __FUNCTION__);
 
-       if (init) {
-               printk (KERN_ERR "Module already initialized!\n");
-               return 0;
+       /* FIXME: Only OFDM implemented */
+       if (fe->ops.info.type != FE_OFDM) {
+               tuner_info ("DTV type not implemented.\n");
+               return -EINVAL;
        }
-       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;
+       return generic_set_tv_freq(fe, p->frequency, T_DIGITAL_TV,
+                                               0, /* NOT USED */
+                                               p->u.ofdm.bandwidth);
 
-       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,
-                            struct dvb_frontend_parameters *p)
+static int xc2028_dvb_release(struct dvb_frontend *fe)
 {
-       struct i2c_client *c = fe->tuner_priv;
+       struct xc2028_data *priv = fe->tuner_priv;
+
+       tuner_info("%s called\n", __FUNCTION__);
 
-       printk(KERN_INFO "xc2028: %s called\n", __FUNCTION__);
+       priv->count--;
 
-       generic_set_tv_freq(c, p->frequency, T_DIGITAL_TV,
-                                            p->u.ofdm.bandwidth);
+       if (!priv->count)
+               kfree (priv);
 
        return 0;
 }
 
-static int xc3028_dvb_release(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;
 
-       fe->tuner_priv = NULL;
+       tuner_info("%s called\n", __FUNCTION__);
 
-       return 0;
-}
-
-static int xc3028_dvb_init(struct dvb_frontend *fe)
-{
-       printk(KERN_INFO "xc2028: %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 @@ static const struct dvb_tuner_ops xc3028_dvb_tuner_ops = {
                        .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");
 
-       return 0;
-}
+       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");
index 34ff4cba131ac5313cd900957ffb171434291d49..d5a18a37d1c50cf470370b8a2614cce84d4a840b 100644 (file)
@@ -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
 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__ */