]> err.no Git - linux-2.6/commitdiff
V4L/DVB (6431): Improve firmware format
authorMauro Carvalho Chehab <mchehab@infradead.org>
Wed, 24 Oct 2007 12:22:08 +0000 (09:22 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Fri, 25 Jan 2008 21:01:05 +0000 (19:01 -0200)
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/video/tuner-xc2028-types.h [new file with mode: 0644]
drivers/media/video/tuner-xc2028.c
drivers/media/video/tuner-xc2028.h

diff --git a/drivers/media/video/tuner-xc2028-types.h b/drivers/media/video/tuner-xc2028-types.h
new file mode 100644 (file)
index 0000000..80b19eb
--- /dev/null
@@ -0,0 +1,99 @@
+/* tuner-xc2028_types
+ *
+ * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+/* xc3028 firmware types */
+
+/* BASE firmware should be loaded before any other firmware */
+#define BASE           (1<<0)
+
+/* F8MHZ marks BASE firmwares for 8 MHz Bandwidth */
+#define F8MHZ          (1<<1)
+
+/* Multichannel Television Sound (MTS)
+   Those firmwares are capable of using xc2038 DSP to decode audio and
+   produce a baseband audio output on some pins of the chip.
+   There are MTS firmwares for the most used video standards. It should be
+   required to use MTS firmwares, depending on the way audio is routed into
+   the bridge chip
+ */
+#define MTS            (1<<2)
+
+/* FIXME: I have no idea what's the difference between
+   D2620 and D2633 firmwares
+ */
+#define D2620          (1<<3)
+#define D2633          (1<<4)
+
+/* DTV firmwares for 6, 7 and 8 MHz
+   DTV6 - 6MHz - ATSC/DVB-C/DVB-T/ISDB-T/DOCSIS
+   DTV8 - 8MHz - DVB-C/DVB-T
+ */
+#define DTV6_ATSC      (1<<5)
+#define DTV6_QAM       (1<<6)
+#define DTV7           (1<<7)
+#define DTV78          (1<<8)
+#define DTV8           (1<<9)
+
+/* There's a FM | BASE firmware + FM specific firmware (std=0) */
+#define        FM              (1<<10)
+
+/* Applies only for FM firmware
+   Makes it use RF input 1 (pin #2) instead of input 2 (pin #4)
+ */
+#define INPUT1         (1<<11)
+
+
+/* LCD firmwares exist only for MTS STD/MN (PAL or NTSC/M)
+       and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr)
+       There are variants both with and without NOGD
+ */
+#define LCD            (1<<12)
+
+/* NOGD firmwares exist only for MTS STD/MN (PAL or NTSC/M)
+       and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr)
+ */
+#define NOGD           (1<<13)
+
+/* Old firmwares were broken into init0 and init1 */
+#define INIT1          (1<<14)
+
+/* Newer types to be moved to videodev2.h */
+
+#define V4L2_STD_SECAM_K3      (0x02000000)
+
+/* Audio types */
+
+#define V4L2_STD_A2_A          (1L<<32)
+#define V4L2_STD_A2_B          (1L<<33)
+#define V4L2_STD_NICAM_A       (1L<<34)
+#define V4L2_STD_NICAM_B       (1L<<35)
+#define V4L2_STD_AM            (1L<<36)
+#define V4L2_STD_BTSC          (1L<<37)
+#define V4L2_STD__EIAJ         (1L<<38)
+
+#define V4L2_STD_A2            (V4L2_STD_A2_A    | V4L2_STD_A2_B)
+#define V4L2_STD_NICAM         (V4L2_STD_NICAM_A | V4L2_STD_NICAM_B)
+
+/* To preserve backward compatibilty,
+   (std & V4L2_STD_AUDIO) = 0 means that ALL audio stds are supported
+ */
+
+#define V4L2_STD_AUDIO         (V4L2_STD_A2    | \
+                                V4L2_STD_NICAM | \
+                                V4L2_STD_AM    | \
+                                V4L2_STD_BTSC  | \
+                                V4L2_STD_EIAJ)
+
+/* Used standards with audio restrictions */
+
+#define V4L2_STD_PAL_BG_A2_A   (V4L2_STD_PAL_BG | V4L2_STD_A2_A)
+#define V4L2_STD_PAL_BG_A2_B   (V4L2_STD_PAL_BG | V4L2_STD_A2_B)
+#define V4L2_STD_PAL_BG_NICAM_A        (V4L2_STD_PAL_BG | V4L2_STD_NICAM_A)
+#define V4L2_STD_PAL_BG_NICAM_B        (V4L2_STD_PAL_BG | V4L2_STD_NICAM_B)
+#define V4L2_STD_PAL_DK_A2     (V4L2_STD_PAL_DK | V4L2_STD_A2)
+#define V4L2_STD_PAL_DK_NICAM  (V4L2_STD_PAL_DK | V4L2_STD_NICAM)
+#define V4L2_STD_SECAM_L_NICAM (V4L2_STD_SECAM_L | V4L2_STD_NICAM)
+#define V4L2_STD_SECAM_L_AM    (V4L2_STD_SECAM_L | V4L2_STD_AM)
index e4c371896de4b7bc9eb1a320ef61d36a780e5a96..7d53d58aafa183338097a27a2740cfd24b9ee567 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/mutex.h>
 #include "tuner-i2c.h"
 #include "tuner-xc2028.h"
+#include "tuner-xc2028-types.h"
 
 #include <linux/dvb/frontend.h>
 #include "dvb_frontend.h"
 #define PREFIX "xc2028 "
 
 static LIST_HEAD(xc2028_list);
-
-/* Firmwares used on tm5600/tm6000 + xc2028/xc3028 */
-
-/* Generic firmwares */
-static const char *firmware_INIT0      = "tm_xc3028_MTS_init0.fw";
-static const char *firmware_8MHZ_INIT0 = "tm_xc3028_8M_MTS_init0.fw";
-static const char *firmware_INIT1      = "tm_xc3028_68M_MTS_init1.fw";
-
-/* Standard-specific firmwares */
-static const char *firmware_6M         = "tm_xc3028_DTV_6M.fw";
-static const char *firmware_7M         = "tm_xc3028_DTV_7M.fw";
-static const char *firmware_8M         = "tm_xc3028_DTV_8M.fw";
-static const char *firmware_B          = "tm_xc3028_B_PAL.fw";
-static const char *firmware_DK         = "tm_xc3028_DK_PAL_MTS.fw";
-static const char *firmware_MN         = "tm_xc3028_MN_BTSC.fw";
+/* struct for storing firmware table */
+struct firmware_description {
+       unsigned int  type;
+       v4l2_std_id   id;
+       unsigned char *ptr;
+       unsigned int  size;
+};
 
 struct xc2028_data {
        struct list_head        xc2028_list;
@@ -46,7 +39,14 @@ struct xc2028_data {
        struct device           *dev;
        void                    *video_dev;
        int                     count;
-       u32                     frequency;
+       __u32                   frequency;
+
+       struct firmware_description *firm;
+       int                     firm_size;
+
+       __u16                   version;
+
+       struct xc2028_ctrl      ctrl;
 
        v4l2_std_id             firm_type;         /* video stds supported
                                                        by current firmware */
@@ -54,6 +54,9 @@ struct xc2028_data {
                                                              6M, 7M or 8M */
        int                     need_load_generic; /* The generic firmware
                                                              were loaded? */
+
+       int                     max_len;        /* Max firmware chunk */
+
        enum tuner_mode mode;
        struct i2c_client       *i2c_client;
 
@@ -102,92 +105,263 @@ static int xc2028_get_reg(struct xc2028_data *priv, u16 reg)
        return (buf[1])|(buf[0]<<8);
 }
 
-static int load_firmware (struct dvb_frontend *fe, const char *name)
+static void free_firmware (struct xc2028_data *priv)
 {
-       struct xc2028_data      *priv = fe->tuner_priv;
+       int i;
+
+       if (!priv->firm)
+               return;
+
+       for (i=0;i<priv->firm_size;i++) {
+               if (priv->firm[i].ptr)
+                       kfree(priv->firm[i].ptr);
+       }
+       kfree(priv->firm);
+
+       priv->firm=NULL;
+       priv->need_load_generic = 1;
+}
+
+static int load_all_firmwares (struct dvb_frontend *fe)
+{
+       struct xc2028_data    *priv = fe->tuner_priv;
        const struct firmware *fw=NULL;
        unsigned char         *p, *endp;
-       int                   len=0, rc=0;
-       static const char     firmware_ver[] = "tm6000/xcv v1";
+       int                   rc=0, n, n_array;
+       char                  name[33];
 
        tuner_info("%s called\n", __FUNCTION__);
 
-       tuner_info("Loading firmware %s\n", name);
-       rc = request_firmware(&fw, name, priv->dev);
+       tuner_info("Loading firmware %s\n", priv->ctrl.fname);
+       rc = request_firmware(&fw, priv->ctrl.fname, priv->dev);
        if (rc < 0) {
                if (rc==-ENOENT)
-                       tuner_info("Error: firmware %s not found.\n", name);
+                       tuner_info("Error: firmware %s not found.\n",
+                                  priv->ctrl.fname);
                else
-                       tuner_info("Error %d while requesting firmware %s \n", rc, name);
+                       tuner_info("Error %d while requesting firmware %s \n",
+                                  rc, priv->ctrl.fname);
 
                return rc;
        }
        p=fw->data;
        endp=p+fw->size;
 
-       if(fw->size==0) {
+       if(fw->size<sizeof(name)-1+2) {
                tuner_info("Error: firmware size is zero!\n");
                rc=-EINVAL;
-               goto err;
+               goto done;
        }
-       if (fw->size<sizeof(firmware_ver)-1) {
-               /* Firmware is incorrect */
-               tuner_info("Error: firmware size is less than header (%d<%d)!\n",
-                          (int)fw->size,(int)sizeof(firmware_ver)-1);
-               rc=-EINVAL;
-               goto err;
+
+       memcpy(name,p,sizeof(name)-1);
+       name[sizeof(name)-1]=0;
+       p+=sizeof(name)-1;
+
+       priv->version = le16_to_cpu(*(__u16 *)p);
+       p += 2;
+
+       tuner_info("firmware: %s, ver %d.%d\n", name,
+                                       priv->version>>8, priv->version&0xff);
+
+       if (p+2>endp)
+               goto corrupt;
+
+       n_array = le16_to_cpu(*(__u16 *)p);
+       p += 2;
+
+       tuner_info("there are %d firmwares at %s\n", n_array, priv->ctrl.fname);
+
+       priv->firm=kzalloc(sizeof(*priv->firm)*n_array,GFP_KERNEL);
+
+       if (!fw) {
+               tuner_info("Not enough memory for loading firmware.\n");
+               rc=-ENOMEM;
+               goto done;
        }
 
-       if (memcmp(p,firmware_ver,sizeof(firmware_ver)-1)) {
-               /* Firmware is incorrect */
-               tuner_info("Error: firmware is not for tm5600/6000 + Xcv2028/3028!\n");
-               rc=-EINVAL;
-               goto err;
+       priv->firm_size = n_array;
+       n=-1;
+       while (p<endp) {
+               __u32 type, size;
+               v4l2_std_id id;
+
+               n++;
+               if (n >= n_array) {
+                       tuner_info("Too much firmwares at the file\n");
+                       goto corrupt;
+               }
+
+               /* Checks if there's enough bytes to read */
+               if (p+sizeof(type)+sizeof(id)+sizeof(size)>endp) {
+                       tuner_info("Lost firmware!\n");
+                       goto corrupt;
+               }
+
+               type = le32_to_cpu(*(__u32 *)p);
+               p += sizeof(type);
+
+               id = le64_to_cpu(*(v4l2_std_id *)p);
+               p += sizeof(id);
+
+               size = le32_to_cpu(*(v4l2_std_id *)p);
+               p += sizeof(size);
+
+               if ((!size)||(size+p>endp)) {
+                       tuner_info("Firmware type %x, id %lx corrupt\n",
+                                  type, (unsigned long) id);
+                       goto corrupt;
+               }
+
+               priv->firm[n].ptr=kzalloc(size,GFP_KERNEL);
+               if (!priv->firm[n].ptr) {
+                       tuner_info("Not enough memory.\n");
+                       rc=-ENOMEM;
+                       goto err;
+               }
+               tuner_info("Loading firmware type %x, id %lx, size=%d.\n",
+                                  type, (unsigned 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;
+
+               p += size;
+       }
+
+       if (n+1 != priv->firm_size) {
+               tuner_info("Firmware file is incomplete!\n");
+               goto corrupt;
+       }
+
+       goto done;
+
+corrupt:
+       rc=-EINVAL;
+       tuner_info("Error: firmware file is corrupted!\n");
+
+err:
+       tuner_info("Releasing loaded firmware file.\n");
+
+       free_firmware(priv);
+
+done:
+       release_firmware(fw);
+       tuner_info("Firmware files loaded.\n");
+
+       return rc;
+}
+
+static int load_firmware (struct dvb_frontend *fe, unsigned int type,
+                         v4l2_std_id *id)
+{
+       struct xc2028_data *priv = fe->tuner_priv;
+       int i, rc;
+       unsigned char *p, *endp, buf[priv->max_len];
+
+       tuner_info("%s called\n", __FUNCTION__);
+
+       if (!priv->firm) {
+               printk (KERN_ERR PREFIX "Error! firmware not loaded\n");
+               return -EINVAL;
+       }
+
+       if ((type == 0) && (*id == 0))
+               *id=V4L2_STD_PAL;
+
+       /* Seek for exact match */
+       for (i=0;i<priv->firm_size;i++) {
+               if ( (type == priv->firm[i].type) &&
+                                               (*id == priv->firm[i].id))
+                       goto found;
+       }
+
+       /* Seek for generic video standard match */
+       for (i=0;i<priv->firm_size;i++) {
+               if ( (type == priv->firm[i].type) && (*id & priv->firm[i].id))
+                       goto found;
+       }
+
+       /*FIXME: Would make sense to seek for type "hint" match ? */
+
+       tuner_info ("Can't find firmware for type=%x, id=%lx\n", type,
+                   (long int)*id);
+       return -EINVAL;
+
+found:
+       *id = priv->firm[i].id;
+       tuner_info ("Found firmware for type=%x, id=%lx\n", type,
+                   (long int)*id);
+
+       p = priv->firm[i].ptr;
+
+       if (!p) {
+               printk(KERN_ERR PREFIX "Firmware pointer were freed!");
+               return -EINVAL;
        }
-       p+=sizeof(firmware_ver)-1;
+       endp = p+priv->firm[i].size;
 
-       while(p<endp) {
-               if ((*p) & 0x80) {
+       while (p<endp) {
+               __u16 size;
+
+               /* Checks if there's enough bytes to read */
+               if (p+sizeof(size)>endp) {
+                       tuner_info("missing bytes\n");
+                       return -EINVAL;
+               }
+
+
+               size = le16_to_cpu(*(__u16 *)p);
+               p += sizeof(size);
+
+               if (size == 0xffff)
+                       return 0;
+
+               if (!size) {
                        /* Special callback command received */
                        rc = priv->tuner_callback(priv->video_dev,
-                                            XC2028_TUNER_RESET, (*p)&0x7f);
+                                            XC2028_TUNER_RESET, 0);
                        if (rc<0) {
                                tuner_info("Error at RESET code %d\n",
                                                                (*p)&0x7f);
-                               goto err;
+                               return -EINVAL;
                        }
-                       p++;
                        continue;
                }
-               len=*p;
-               p++;
-               if (p+len+1>endp) {
-                       /* Firmware is incorrect */
-                       tuner_info("Error: firmware is truncated!\n");
-                       rc=-EINVAL;
-                       goto err;
-               }
-               if (len<=0) {
-                       tuner_info("Error: firmware file is corrupted!\n");
-                       rc=-EINVAL;
-                       goto err;
+
+               /* Checks for a sleep command */
+               if (size & 0x8000) {
+                       msleep (size & 0x7fff);
+                       continue;
                }
 
-               i2c_send(rc, priv, p, len);
-               if (rc<0)
-                       goto err;
-               p+=len;
+               if ((size + p > endp)) {
+                       tuner_info("missing bytes: need %d, have %d\n",
+                                       size, (int)(endp-p));
+                       return -EINVAL;
+               }
 
-               if (*p)
-                       msleep(*p);
+               buf[0] = *p;
                p++;
-       }
+               size--;
 
+               /* Sends message chunks */
+               while (size>0) {
+                       int len = (size<priv->max_len-1)?size:priv->max_len-1;
 
-err:
-       release_firmware(fw);
+                       memcpy(buf+1, p, len);
 
-       return rc;
+                       i2c_send(rc, priv, buf, len+1);
+                       if (rc<0) {
+                               tuner_info("%d returned from send\n",rc);
+                               return -EINVAL;
+                       }
+
+                       p += len;
+                       size -= len;
+               }
+       }
+       return -EINVAL;
 }
 
 static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
@@ -196,11 +370,21 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
 {
        struct xc2028_data      *priv = fe->tuner_priv;
        int                     rc, version;
-       const char              *name;
-       int change_digital_bandwidth;
+       v4l2_std_id             std0=0;
+       unsigned int            type0=0,type=0;
+       int                     change_digital_bandwidth;
 
        tuner_info("%s called\n", __FUNCTION__);
 
+       if (!priv->firm) {
+               if (!priv->ctrl.fname)
+                       return -EINVAL;
+
+               rc=load_all_firmwares(fe);
+               if (rc<0)
+                       return rc;
+       }
+
        tuner_info( "I am in mode %u and I should switch to mode %i\n",
                                                    priv->mode, new_mode);
 
@@ -213,23 +397,31 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
        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);
+                   bandwidth);
 
        if (priv->need_load_generic) {
-               if (priv->bandwidth==8)
-                       name = firmware_8MHZ_INIT0;
-               else
-                       name = firmware_INIT0;
-
                /* Reset is needed before loading firmware */
                rc = priv->tuner_callback(priv->video_dev,
                                          XC2028_TUNER_RESET, 0);
                if (rc<0)
                        return rc;
 
-               rc = load_firmware(fe,name);
-               if (rc<0)
+               type0=BASE;
+
+               if (priv->ctrl.type == XC2028_FIRM_MTS)
+                       type0 |= MTS;
+
+               if (priv->bandwidth==8)
+                       type0 |= F8MHZ;
+
+               /* FIXME: How to load FM and FM|INPUT1 firmwares? */
+
+               rc = load_firmware(fe, type0, &std0);
+               if (rc<0) {
+                       tuner_info("Error %d while loading generic firmware\n",
+                                  rc);
                        return rc;
+               }
 
                priv->need_load_generic=0;
                priv->firm_type=0;
@@ -241,49 +433,53 @@ static int check_firmware(struct dvb_frontend *fe, enum tuner_mode new_mode,
        tuner_info("I should change bandwidth %u\n",
                                                   change_digital_bandwidth);
 
-       /* FIXME: t->std makes no sense here */
        if (change_digital_bandwidth) {
+
+               /*FIXME: Should allow selecting between D2620 and D2633 */
+               type |= D2620;
+
+               /* FIXME: When should select a DTV78 firmware?
+                */
                switch(bandwidth) {
-                       case BANDWIDTH_8_MHZ:
-                               std = V4L2_STD_DTV_8MHZ;
+               case BANDWIDTH_8_MHZ:
+                       type |= DTV8;
                        break;
-
-                       case BANDWIDTH_7_MHZ:
-                               std = V4L2_STD_DTV_7MHZ;
+               case BANDWIDTH_7_MHZ:
+                       type |= DTV7;
                        break;
-
-                       case BANDWIDTH_6_MHZ:
-                               std = V4L2_STD_DTV_6MHZ;
+               case BANDWIDTH_6_MHZ:
+                       /* FIXME: Should allow select also ATSC */
+                       type |= DTV6_QAM;
                        break;
 
-                       default:
-                               tuner_info("error: bandwidth not supported.\n");
+               default:
+                       tuner_info("error: bandwidth not supported.\n");
                };
                priv->bandwidth = bandwidth;
        }
 
+       /* Load INIT1, if needed */
+       tuner_info("Trying to load init1 firmware\n");
+       type0 = BASE | INIT1 | priv->ctrl.type;
+       if (priv->ctrl.type == XC2028_FIRM_MTS)
+               type0 |= MTS;
+
+       /* FIXME: Should handle errors - if INIT1 found */
+       rc = load_firmware(fe, type0, &std0);
+
+       /* FIXME: Should add support for FM radio
+        */
+
+       if (priv->ctrl.type == XC2028_FIRM_MTS)
+               type |= MTS;
+
+       tuner_info("firmware standard to load: %08lx\n",(unsigned long) std);
        if (priv->firm_type & std) {
-               tuner_info("xc3028: no need to load a std-specific firmware.\n");
+               tuner_info("no need to load a std-specific firmware.\n");
                return 0;
        }
 
-       rc = load_firmware(fe,firmware_INIT1);
-
-       if (std & V4L2_STD_MN)
-               name=firmware_MN;
-       else if (std & V4L2_STD_DTV_6MHZ)
-               name=firmware_6M;
-       else if (std & V4L2_STD_DTV_7MHZ)
-               name=firmware_7M;
-       else if (std & V4L2_STD_DTV_8MHZ)
-               name=firmware_8M;
-       else if (std & V4L2_STD_PAL_B)
-               name=firmware_B;
-       else
-               name=firmware_DK;
-
-       tuner_info("loading firmware named %s.\n", name);
-       rc = load_firmware(fe, name);
+       rc = load_firmware(fe, type, &std);
        if (rc<0)
                return rc;
 
@@ -341,11 +537,11 @@ static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */,
 
        tuner_info("%s called\n", __FUNCTION__);
 
+       mutex_lock(&priv->lock);
+
        /* HACK: It seems that specific firmware need to be reloaded
           when freq is changed */
 
-       mutex_lock(&priv->lock);
-
        priv->firm_type=0;
 
        /* Reset GPIO 1 */
@@ -365,7 +561,13 @@ static int generic_set_tv_freq(struct dvb_frontend *fe, u32 freq /* in Hz */,
        div = (freq - offset + DIV/2)/DIV;
 
        /* CMD= Set frequency */
-       send_seq(priv, {0x00, 0x02, 0x00, 0x00});
+
+       if (priv->version<0x0202) {
+               send_seq(priv, {0x00, 0x02, 0x00, 0x00});
+       } else {
+               send_seq(priv, {0x80, 0x02, 0x00, 0x00});
+       }
+
        rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
        if (rc<0)
                goto ret;
@@ -436,8 +638,13 @@ static int xc2028_dvb_release(struct dvb_frontend *fe)
 
        priv->count--;
 
-       if (!priv->count)
+       if (!priv->count) {
+               if (priv->ctrl.fname)
+                       kfree(priv->ctrl.fname);
+
+               free_firmware(priv);
                kfree (priv);
+       }
 
        return 0;
 }
@@ -453,6 +660,32 @@ static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *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;
+
+       tuner_info("%s called\n", __FUNCTION__);
+
+       priv->ctrl.type = p->type;
+
+       if (p->fname) {
+               if (priv->ctrl.fname)
+                       kfree(priv->ctrl.fname);
+
+               priv->ctrl.fname = kmalloc(strlen(p->fname)+1, GFP_KERNEL);
+               if (!priv->ctrl.fname)
+                       return -ENOMEM;
+
+               free_firmware(priv);
+               strcpy(priv->ctrl.fname, p->fname);
+       }
+
+       tuner_info("%s OK\n", __FUNCTION__);
+
+       return 0;
+}
+
 static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
        .info = {
                        .name           = "Xceive XC3028",
@@ -461,6 +694,7 @@ static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
                        .frequency_step =     50000,
                },
 
+       .set_config        = xc2028_set_config,
        .set_analog_params = xc2028_set_tv_freq,
        .release           = xc2028_dvb_release,
        .get_frequency     = xc2028_get_frequency,
@@ -513,6 +747,8 @@ int xc2028_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap,
                priv->dev = dev;
                priv->video_dev = video_dev;
                priv->tuner_callback = tuner_callback;
+               priv->max_len = 13;
+
 
                mutex_init(&priv->lock);
 
index d5a18a37d1c50cf470370b8a2614cce84d4a840b..f4856f07bd08a31e5e0e89934efda319cc61f21f 100644 (file)
@@ -9,13 +9,22 @@
 
 #include "dvb_frontend.h"
 
+#define XC2028_DEFAULT_FIRMWARE "xc3028-v27.fw"
+
+enum xc2028_firm_type {
+       XC2028_FIRM_NORMAL,
+       XC2028_FIRM_MTS,
+};
+
+struct xc2028_ctrl {
+       enum xc2028_firm_type   type;
+       char                    *fname;
+};
+
 /* xc2028 commands for callback */
 #define XC2028_TUNER_RESET     0
 #define XC2028_RESET_CLK       1
 
-struct dvb_frontend;
-struct i2c_client;
-
 #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,