]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/staging/tm6000/tm6000-cards.c
V4L/DVB: tm6000: only register after initialized
[net-next-2.6.git] / drivers / staging / tm6000 / tm6000-cards.c
index b0719a52ef4212e53194c292af341d99b2f9499a..43620a3f5ec1c0e2d694fa8acea4c9fbe37c8d99 100644 (file)
 #include <linux/version.h>
 #include <media/v4l2-common.h>
 #include <media/tuner.h>
+#include <media/tvaudio.h>
+#include <media/i2c-addr.h>
 
 #include "tm6000.h"
-
-#define TM6000_BOARD_UNKNOWN           0
-#define TM5600_BOARD_GENERIC           1
-#define TM6000_BOARD_GENERIC           2
-#define TM5600_BOARD_10MOONS_UT821     3
-#define TM6000_BOARD_10MOONS_UT330     4
-#define TM6000_BOARD_ADSTECH_DUAL_TV   5
+#include "tm6000-regs.h"
+#include "tuner-xc2028.h"
+#include "xc5000.h"
+
+#define TM6000_BOARD_UNKNOWN                   0
+#define TM5600_BOARD_GENERIC                   1
+#define TM6000_BOARD_GENERIC                   2
+#define TM6010_BOARD_GENERIC                   3
+#define TM5600_BOARD_10MOONS_UT821             4
+#define TM5600_BOARD_10MOONS_UT330             5
+#define TM6000_BOARD_ADSTECH_DUAL_TV           6
+#define TM6000_BOARD_FREECOM_AND_SIMILAR       7
+#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV      8
+#define TM6010_BOARD_HAUPPAUGE_900H            9
+#define TM6010_BOARD_BEHOLD_WANDER             10
+#define TM6010_BOARD_BEHOLD_VOYAGER            11
+#define TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE        12
 
 #define TM6000_MAXBOARDS        16
 static unsigned int card[]     = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
 
 module_param_array(card,  int, NULL, 0444);
 
+static unsigned long tm6000_devused;
+
+
 struct tm6000_board {
        char            *name;
 
        struct tm6000_capabilities caps;
 
+       enum            tm6000_devtype type;    /* variant of the chipset */
        int             tuner_type;     /* type of the tuner */
        int             tuner_addr;     /* tuner address */
+       int             demod_addr;     /* demodulator address */
+       int             gpio_addr_tun_reset;    /* GPIO used for tuner reset */
 };
 
-
 struct tm6000_board tm6000_boards[] = {
        [TM6000_BOARD_UNKNOWN] = {
                .name         = "Unknown tm6000 video grabber",
                .caps = {
                        .has_tuner    = 1,
                },
+               .gpio_addr_tun_reset = TM6000_GPIO_1,
        },
        [TM5600_BOARD_GENERIC] = {
                .name         = "Generic tm5600 board",
+               .type         = TM5600,
                .tuner_type   = TUNER_XC2028,
-               .tuner_addr   = 0xc2,
+               .tuner_addr   = 0xc2 >> 1,
                .caps = {
                        .has_tuner      = 1,
                },
+               .gpio_addr_tun_reset = TM6000_GPIO_1,
        },
        [TM6000_BOARD_GENERIC] = {
                .name         = "Generic tm6000 board",
                .tuner_type   = TUNER_XC2028,
-               .tuner_addr   = 0xc2,
+               .tuner_addr   = 0xc2 >> 1,
+               .caps = {
+                       .has_tuner      = 1,
+                       .has_dvb        = 1,
+               },
+               .gpio_addr_tun_reset = TM6000_GPIO_1,
+       },
+       [TM6010_BOARD_GENERIC] = {
+               .name         = "Generic tm6010 board",
+               .type         = TM6010,
+               .tuner_type   = TUNER_XC2028,
+               .tuner_addr   = 0xc2 >> 1,
                .caps = {
                        .has_tuner      = 1,
                        .has_dvb        = 1,
                },
+               .gpio_addr_tun_reset = TM6010_GPIO_4,
        },
        [TM5600_BOARD_10MOONS_UT821] = {
                .name         = "10Moons UT 821",
                .tuner_type   = TUNER_XC2028,
-               .tuner_addr   = 0xc2,
+               .type         = TM5600,
+               .tuner_addr   = 0xc2 >> 1,
                .caps = {
                        .has_tuner    = 1,
                        .has_eeprom   = 1,
                },
+               .gpio_addr_tun_reset = TM6000_GPIO_1,
        },
-       [TM6000_BOARD_10MOONS_UT330] = {
+       [TM5600_BOARD_10MOONS_UT330] = {
                .name         = "10Moons UT 330",
-               .tuner_type   = TUNER_XC2028,
-               .tuner_addr   = 0xc8,
+               .tuner_type   = TUNER_PHILIPS_FQ1216AME_MK4,
+               .tuner_addr   = 0xc8 >> 1,
                .caps = {
                        .has_tuner    = 1,
-                       .has_dvb      = 1,
-                       .has_zl10353  = 1,
+                       .has_dvb      = 0,
+                       .has_zl10353  = 0,
                        .has_eeprom   = 1,
                },
        },
        [TM6000_BOARD_ADSTECH_DUAL_TV] = {
                .name         = "ADSTECH Dual TV USB",
                .tuner_type   = TUNER_XC2028,
-               .tuner_addr   = 0xc8,
+               .tuner_addr   = 0xc8 >> 1,
                .caps = {
                        .has_tuner    = 1,
                        .has_tda9874  = 1,
@@ -107,15 +141,285 @@ struct tm6000_board tm6000_boards[] = {
                        .has_eeprom   = 1,
                },
        },
+       [TM6000_BOARD_FREECOM_AND_SIMILAR] = {
+               .name         = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual",
+               .tuner_type   = TUNER_XC2028, /* has a XC3028 */
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 0,
+                       .has_remote   = 1,
+               },
+               .gpio_addr_tun_reset = TM6000_GPIO_4,
+       },
+       [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = {
+               .name         = "ADSTECH Mini Dual TV USB",
+               .tuner_type   = TUNER_XC2028, /* has a XC3028 */
+               .tuner_addr   = 0xc8 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 0,
+               },
+               .gpio_addr_tun_reset = TM6000_GPIO_4,
+       },
+       [TM6010_BOARD_HAUPPAUGE_900H] = {
+               .name         = "Hauppauge HVR-900H",
+               .tuner_type   = TUNER_XC2028, /* has a XC3028 */
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .type         = TM6010,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 1,
+               },
+               .gpio_addr_tun_reset = TM6000_GPIO_2,
+       },
+       [TM6010_BOARD_BEHOLD_WANDER] = {
+               .name         = "Beholder Wander DVB-T/TV/FM USB2.0",
+               .tuner_type   = TUNER_XC5000,
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .type         = TM6010,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 1,
+                       .has_remote   = 1,
+               },
+               .gpio_addr_tun_reset = TM6000_GPIO_2,
+       },
+       [TM6010_BOARD_BEHOLD_VOYAGER] = {
+               .name         = "Beholder Voyager TV/FM USB2.0",
+               .tuner_type   = TUNER_XC5000,
+               .tuner_addr   = 0xc2 >> 1,
+               .type         = TM6010,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 0,
+                       .has_zl10353  = 0,
+                       .has_eeprom   = 1,
+                       .has_remote   = 1,
+               },
+               .gpio_addr_tun_reset = TM6000_GPIO_2,
+       },
+       [TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE] = {
+               .name         = "Terratec Cinergy Hybrid XE",
+               .tuner_type   = TUNER_XC2028, /* has a XC3028 */
+               .tuner_addr   = 0xc2 >> 1,
+               .demod_addr   = 0x1e >> 1,
+               .type         = TM6010,
+               .caps = {
+                       .has_tuner    = 1,
+                       .has_dvb      = 1,
+                       .has_zl10353  = 1,
+                       .has_eeprom   = 1,
+                       .has_remote   = 1,
+               },
+               .gpio_addr_tun_reset = TM6010_GPIO_2,
+       }
 };
 
 /* table of devices that work with this driver */
 struct usb_device_id tm6000_id_table [] = {
        { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_10MOONS_UT821 },
+       { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC },
        { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
+       { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR },
+       { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV },
+       { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H },
+       { USB_DEVICE(0x6000, 0xdec0), .driver_info = TM6010_BOARD_BEHOLD_WANDER },
+       { USB_DEVICE(0x6000, 0xdec1), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER },
+       { USB_DEVICE(0x0ccd, 0x0086), .driver_info = TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE },
        { },
 };
 
+/* Tuner callback to provide the proper gpio changes needed for xc2028 */
+
+int tm6000_tuner_callback(void *ptr, int component, int command, int arg)
+{
+       int rc=0;
+       struct tm6000_core *dev = ptr;
+
+       if (dev->tuner_type!=TUNER_XC2028)
+               return 0;
+
+       switch (command) {
+       case XC2028_RESET_CLK:
+               tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT,
+                                       0x02, arg);
+               msleep(10);
+               rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
+                                       TM6000_GPIO_CLK, 0);
+               if (rc<0)
+                       return rc;
+               msleep(10);
+               rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
+                                       TM6000_GPIO_CLK, 1);
+               break;
+       case XC2028_TUNER_RESET:
+               /* Reset codes during load firmware */
+               switch (arg) {
+               case 0:
+                       tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
+                                       dev->tuner_reset_gpio, 0x00);
+                       msleep(130);
+                       tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
+                                       dev->tuner_reset_gpio, 0x01);
+                       msleep(130);
+                       break;
+               case 1:
+                       tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT,
+                                               0x02, 0x01);
+                       msleep(10);
+                       break;
+
+               case 2:
+                       rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
+                                               TM6000_GPIO_CLK, 0);
+                       if (rc<0)
+                               return rc;
+                       msleep(100);
+                       rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
+                                               TM6000_GPIO_CLK, 1);
+                       msleep(100);
+                       break;
+               }
+       }
+       return (rc);
+}
+
+int tm6000_cards_setup(struct tm6000_core *dev)
+{
+       int i, rc;
+
+       /*
+        * Board-specific initialization sequence. Handles all GPIO
+        * initialization sequences that are board-specific.
+        * Up to now, all found devices use GPIO1 and GPIO4 at the same way.
+        * Probably, they're all based on some reference device. Due to that,
+        * there's a common routine at the end to handle those GPIO's. Devices
+        * that use different pinups or init sequences can just return at
+        * the board-specific session.
+        */
+       switch (dev->model) {
+       case TM6010_BOARD_HAUPPAUGE_900H:
+               /* Turn xceive 3028 on */
+               tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6010_GPIO_3, 0x01);
+               msleep(11);
+               break;
+       default:
+               break;
+       }
+
+       /*
+        * Default initialization. Most of the devices seem to use GPIO1
+        * and GPIO4.on the same way, so, this handles the common sequence
+        * used by most devices.
+        * If a device uses a different sequence or different GPIO pins for
+        * reset, just add the code at the board-specific part
+        */
+       for (i = 0; i < 2; i++) {
+               rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                       dev->tuner_reset_gpio, 0x00);
+               if (rc < 0) {
+                       printk(KERN_ERR "Error %i doing GPIO1 reset\n", rc);
+                       return rc;
+               }
+
+               msleep(10); /* Just to be conservative */
+               rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN,
+                                       dev->tuner_reset_gpio, 0x01);
+               if (rc < 0) {
+                       printk(KERN_ERR "Error %i doing GPIO1 reset\n", rc);
+                       return rc;
+               }
+
+               msleep(10);
+               rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_4, 0);
+               if (rc < 0) {
+                       printk(KERN_ERR "Error %i doing GPIO4 reset\n", rc);
+                       return rc;
+               }
+
+               msleep(10);
+               rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_4, 1);
+               if (rc < 0) {
+                       printk(KERN_ERR "Error %i doing GPIO4 reset\n", rc);
+                       return rc;
+               }
+
+               if (!i) {
+                       rc = tm6000_get_reg16(dev, 0x40, 0, 0);
+                       if (rc >= 0)
+                               printk(KERN_DEBUG "board=%d\n", rc);
+               }
+       }
+
+       msleep(50);
+
+       return 0;
+};
+
+static void tm6000_config_tuner (struct tm6000_core *dev)
+{
+       struct tuner_setup           tun_setup;
+
+       /* Load tuner module */
+       v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+               "tuner", "tuner",dev->tuner_addr, NULL);
+
+       memset(&tun_setup, 0, sizeof(tun_setup));
+       tun_setup.type   = dev->tuner_type;
+       tun_setup.addr   = dev->tuner_addr;
+       tun_setup.mode_mask = T_ANALOG_TV | T_RADIO | T_DIGITAL_TV;
+       tun_setup.tuner_callback = tm6000_tuner_callback;
+
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+
+       if (dev->tuner_type == TUNER_XC2028) {
+               struct v4l2_priv_tun_config  xc2028_cfg;
+               struct xc2028_ctrl           ctl;
+
+               memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+               memset (&ctl,0,sizeof(ctl));
+
+               ctl.input1 = 1;
+               ctl.read_not_reliable = 0;
+               ctl.msleep = 10;
+               ctl.demod = XC3028_FE_ZARLINK456;
+               ctl.vhfbw7 = 1;
+               ctl.uhfbw8 = 1;
+               xc2028_cfg.tuner = TUNER_XC2028;
+               xc2028_cfg.priv  = &ctl;
+
+               switch(dev->model) {
+               case TM6010_BOARD_HAUPPAUGE_900H:
+               case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+                       ctl.fname = "xc3028L-v36.fw";
+                       break;
+               default:
+                       if (dev->dev_type == TM6010)
+                               ctl.fname = "xc3028-v27.fw";
+                       else
+                               ctl.fname = "tm6000-xc3028.fw";
+               }
+
+               printk(KERN_INFO "Setting firmware parameters for xc2028\n");
+
+               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config,
+                                    &xc2028_cfg);
+       }
+}
+
 static int tm6000_init_dev(struct tm6000_core *dev)
 {
        struct v4l2_frequency f;
@@ -126,8 +430,12 @@ static int tm6000_init_dev(struct tm6000_core *dev)
        mutex_lock(&dev->lock);
 
        /* Initializa board-specific data */
+       dev->dev_type   = tm6000_boards[dev->model].type;
        dev->tuner_type = tm6000_boards[dev->model].tuner_type;
        dev->tuner_addr = tm6000_boards[dev->model].tuner_addr;
+       dev->tuner_reset_gpio = tm6000_boards[dev->model].gpio_addr_tun_reset;
+
+       dev->demod_addr = tm6000_boards[dev->model].demod_addr;
 
        dev->caps = tm6000_boards[dev->model].caps;
 
@@ -136,29 +444,62 @@ static int tm6000_init_dev(struct tm6000_core *dev)
        if (rc<0)
                goto err;
 
+       rc = v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev);
+       if (rc < 0)
+               goto err;
+
        /* register i2c bus */
        rc=tm6000_i2c_register(dev);
        if (rc<0)
                goto err;
 
-       /* register and initialize V4L2 */
-       rc=tm6000_v4l2_register(dev);
-       if (rc<0)
-               goto err;
+       /* Default values for STD and resolutions */
+       dev->width = 720;
+       dev->height = 480;
+       dev->norm = V4L2_STD_PAL_M;
 
-       /* Request tuner */
-       request_module ("tuner");
-//     norm=V4L2_STD_NTSC_M;
-       dev->norm=V4L2_STD_PAL_M;
-       tm6000_i2c_call_clients(dev, VIDIOC_S_STD, &dev->norm);
+       /* Configure tuner */
+       tm6000_config_tuner (dev);
 
-       /* configure tuner */
+       /* Set video standard */
+       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+
+       /* Set tuner frequency - also loads firmware on xc2028/xc3028 */
        f.tuner = 0;
        f.type = V4L2_TUNER_ANALOG_TV;
        f.frequency = 3092;     /* 193.25 MHz */
        dev->freq = f.frequency;
+       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+
+       if (dev->caps.has_tda9874)
+               v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+                       "tvaudio", "tvaudio", I2C_ADDR_TDA9874, NULL);
+
+       /* register and initialize V4L2 */
+       rc=tm6000_v4l2_register(dev);
+       if (rc<0)
+               goto err;
+
+       if(dev->caps.has_dvb) {
+               dev->dvb = kzalloc(sizeof(*(dev->dvb)), GFP_KERNEL);
+               if(!dev->dvb) {
+                       rc = -ENOMEM;
+                       goto err2;
+               }
+
+#ifdef CONFIG_VIDEO_TM6000_DVB
+               rc = tm6000_dvb_register(dev);
+               if(rc < 0) {
+                       kfree(dev->dvb);
+                       dev->dvb = NULL;
+                       goto err2;
+               }
+#endif
+       }
+       return 0;
 
-       tm6000_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
+err2:
+       v4l2_device_unregister(&dev->v4l2_dev);
 
 err:
        mutex_unlock(&dev->lock);
@@ -213,7 +554,7 @@ static int tm6000_usb_probe(struct usb_interface *interface,
        /* Check to see next free device and mark as used */
        nr=find_first_zero_bit(&tm6000_devused,TM6000_MAXBOARDS);
        if (nr >= TM6000_MAXBOARDS) {
-               printk ("tm6000: Supports only %i em28xx boards.\n",TM6000_MAXBOARDS);
+               printk ("tm6000: Supports only %i tm60xx boards.\n",TM6000_MAXBOARDS);
                usb_put_dev(usbdev);
                return -ENOMEM;
        }
@@ -229,10 +570,15 @@ static int tm6000_usb_probe(struct usb_interface *interface,
 
        /* Increment usage count */
        tm6000_devused|=1<<nr;
+       snprintf(dev->name, 29, "tm6000 #%d", nr);
 
-       dev->udev= usbdev;
        dev->model=id->driver_info;
-       snprintf(dev->name, 29, "tm6000 #%d", nr);
+       if ((card[nr]>=0) && (card[nr]<ARRAY_SIZE(tm6000_boards))) {
+               dev->model=card[nr];
+       }
+
+       INIT_LIST_HEAD(&dev->tm6000_corelist);
+       dev->udev= usbdev;
        dev->devno=nr;
 
        switch (usbdev->speed) {
@@ -297,15 +643,6 @@ static int tm6000_usb_probe(struct usb_interface *interface,
                }
        }
 
-       if (interface->altsetting->desc.bAlternateSetting) {
-               printk("selecting alt setting %d\n",
-                      interface->altsetting->desc.bAlternateSetting);
-               rc = usb_set_interface (usbdev,
-                               interface->altsetting->desc.bInterfaceNumber,
-                               interface->altsetting->desc.bAlternateSetting);
-               if (rc<0)
-                       goto err;
-       }
 
        printk("tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n",
                speed,
@@ -334,6 +671,8 @@ static int tm6000_usb_probe(struct usb_interface *interface,
        return 0;
 
 err:
+       printk("tm6000: Error %d while registering\n", rc);
+
        tm6000_devused&=~(1<<nr);
        usb_put_dev(usbdev);
 
@@ -354,21 +693,31 @@ static void tm6000_usb_disconnect(struct usb_interface *interface)
        if (!dev)
                return;
 
-       tm6000_i2c_unregister(dev);
-
        printk("tm6000: disconnecting %s\n", dev->name);
 
        mutex_lock(&dev->lock);
 
-       tm6000_i2c_unregister(dev);
+#ifdef CONFIG_VIDEO_TM6000_DVB
+       if(dev->dvb) {
+               tm6000_dvb_unregister(dev);
+               kfree(dev->dvb);
+       }
+#endif
 
        tm6000_v4l2_unregister(dev);
 
+       tm6000_i2c_unregister(dev);
+
+       v4l2_device_unregister(&dev->v4l2_dev);
+
 //     wake_up_interruptible_all(&dev->open);
 
        dev->state |= DEV_DISCONNECTED;
 
+       usb_put_dev(dev->udev);
+
        mutex_unlock(&dev->lock);
+       kfree(dev);
 }
 
 static struct usb_driver tm6000_usb_driver = {