Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6
[pandora-kernel.git] / drivers / staging / tm6000 / tm6000-core.c
index 80f2bf0..40a0206 100644 (file)
@@ -1,23 +1,23 @@
 /*
-   tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices
-
-   Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
-
-   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
-       - DVB-T support
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation version 2
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*  tm6000-core.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
*  Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
*  Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
*      - DVB-T support
+ *
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation version 2
+ *
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
+ *
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
 #include <media/v4l2-common.h>
 #include <media/tuner.h>
 
-#define USB_TIMEOUT    5*HZ /* ms */
+#define USB_TIMEOUT    (5 * HZ) /* ms */
 
 int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
                          u16 value, u16 index, u8 *buf, u16 len)
 {
        int          ret, i;
        unsigned int pipe;
-       static int   ini = 0, last = 0, n = 0;
        u8           *data = NULL;
 
        if (len)
@@ -52,19 +51,12 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
        }
 
        if (tm6000_debug & V4L2_DEBUG_I2C) {
-               if (!ini)
-                       last = ini = jiffies;
-
-               printk("%06i (dev %p, pipe %08x): ", n, dev->udev, pipe);
+               printk("(dev %p, pipe %08x): ", dev->udev, pipe);
 
-               printk("%s: %06u ms %06u ms %02x %02x %02x %02x %02x %02x %02x %02x ",
+               printk("%s: %02x %02x %02x %02x %02x %02x %02x %02x ",
                        (req_type & USB_DIR_IN) ? " IN" : "OUT",
-                       jiffies_to_msecs(jiffies-last),
-                       jiffies_to_msecs(jiffies-ini),
                        req_type, req, value&0xff, value>>8, index&0xff,
                        index>>8, len&0xff, len>>8);
-               last = jiffies;
-               n++;
 
                if (!(req_type & USB_DIR_IN)) {
                        printk(">>> ");
@@ -186,21 +178,17 @@ void tm6000_set_fourcc_format(struct tm6000_core *dev)
        }
 }
 
-int tm6000_init_analog_mode(struct tm6000_core *dev)
+static void tm6000_set_vbi(struct tm6000_core *dev)
 {
-       if (dev->dev_type == TM6010) {
-               int val;
+       /*
+        * FIXME:
+        * VBI lines and start/end are different between 60Hz and 50Hz
+        * So, it is very likely that we need to change the config to
+        * something that takes it into account, doing something different
+        * if (dev->norm & V4L2_STD_525_60)
+        */
 
-               /* Enable video */
-               val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0);
-               val |= 0x60;
-               tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
-               val = tm6000_get_reg(dev,
-                       TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0);
-               val &= ~0x40;
-               tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val);
-
-               /* Init teletext */
+       if (dev->dev_type == TM6010) {
                tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
                tm6000_set_reg(dev, TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27);
                tm6000_set_reg(dev, TM6010_REQ07_R42_VBI_DATA_HIGH_LEVEL, 0x55);
@@ -249,44 +237,26 @@ int tm6000_init_analog_mode(struct tm6000_core *dev)
                tm6000_set_reg(dev, TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c);
                tm6000_set_reg(dev, TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01);
                tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x00);
+       }
+}
 
+int tm6000_init_analog_mode(struct tm6000_core *dev)
+{
+       struct v4l2_frequency f;
+
+       if (dev->dev_type == TM6010) {
+               int val;
+
+               /* Enable video */
+               val = tm6000_get_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0);
+               val |= 0x60;
+               tm6000_set_reg(dev, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val);
+               val = tm6000_get_reg(dev,
+                       TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0);
+               val &= ~0x40;
+               tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val);
 
-               /* Init audio */
-               tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
-               tm6000_set_reg(dev, TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL, 0xa0);
-               tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD, 0x05);
-               tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x06);
-               tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL, 0x08);
-               tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0x91);
-               tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1, 0x20);
-               tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2, 0x12);
-               tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES, 0x20);
-               tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1, 0xf0);
-               tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2, 0x80);
-               tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1, 0xc0);
-               tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2, 0x80);
-               tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12);
-               tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T, 0xfe);
-               tm6000_set_reg(dev, TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20);
-               tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR, 0x14);
-               tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX, 0xfe);
-               tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN, 0x01);
-               tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL, 0xa0);
-               tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN, 0x32);
-               tm6000_set_reg(dev, TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64);
-               tm6000_set_reg(dev, TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20);
-               tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c, 0x00);
-               tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
-               tm6000_set_reg(dev, TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3);
-               tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD, 0x00);
-               tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
+               tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc);
 
        } else {
                /* Enables soft reset */
@@ -325,15 +295,22 @@ int tm6000_init_analog_mode(struct tm6000_core *dev)
 
        /* Tuner firmware can now be loaded */
 
-       /*FIXME: Hack!!! */
-       struct v4l2_frequency f;
-       mutex_lock(&dev->lock);
+       /*
+        * FIXME: This is a hack! xc3028 "sleeps" when no channel is detected
+        * for more than a few seconds. Not sure why, as this behavior does
+        * not happen on other devices with xc3028. So, I suspect that it
+        * is yet another bug at tm6000. After start sleeping, decoding 
+        * doesn't start automatically. Instead, it requires some
+        * I2C commands to wake it up. As we want to have image at the
+        * beginning, we needed to add this hack. The better would be to
+        * discover some way to make tm6000 to wake up without this hack.
+        */
        f.frequency = dev->freq;
        v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
-       mutex_unlock(&dev->lock);
 
        msleep(100);
        tm6000_set_standard(dev, &dev->norm);
+       tm6000_set_vbi(dev);
        tm6000_set_audio_bitrate(dev, 48000);
 
        /* switch dvb led off */
@@ -361,7 +338,6 @@ int tm6000_init_digital_mode(struct tm6000_core *dev)
                tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28);
                tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc);
                tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff);
-               tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe);
                tm6000_read_write_usb(dev, 0xc0, 0x0e, 0x00c2, 0x0008, buf, 2);
                printk(KERN_INFO"buf %#x %#x\n", buf[0], buf[1]);
        } else  {
@@ -660,7 +636,6 @@ void tm6000_add_into_devlist(struct tm6000_core *dev)
  */
 
 static LIST_HEAD(tm6000_extension_devlist);
-static DEFINE_MUTEX(tm6000_extension_devlist_lock);
 
 int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type,
                        char *buf, int size)
@@ -684,14 +659,12 @@ int tm6000_register_extension(struct tm6000_ops *ops)
        struct tm6000_core *dev = NULL;
 
        mutex_lock(&tm6000_devlist_mutex);
-       mutex_lock(&tm6000_extension_devlist_lock);
        list_add_tail(&ops->next, &tm6000_extension_devlist);
        list_for_each_entry(dev, &tm6000_devlist, devlist) {
                ops->init(dev);
                printk(KERN_INFO "%s: Initialized (%s) extension\n",
                       dev->name, ops->name);
        }
-       mutex_unlock(&tm6000_extension_devlist_lock);
        mutex_unlock(&tm6000_devlist_mutex);
        return 0;
 }
@@ -705,10 +678,8 @@ void tm6000_unregister_extension(struct tm6000_ops *ops)
        list_for_each_entry(dev, &tm6000_devlist, devlist)
                ops->fini(dev);
 
-       mutex_lock(&tm6000_extension_devlist_lock);
        printk(KERN_INFO "tm6000: Remove (%s) extension\n", ops->name);
        list_del(&ops->next);
-       mutex_unlock(&tm6000_extension_devlist_lock);
        mutex_unlock(&tm6000_devlist_mutex);
 }
 EXPORT_SYMBOL(tm6000_unregister_extension);
@@ -717,26 +688,26 @@ void tm6000_init_extension(struct tm6000_core *dev)
 {
        struct tm6000_ops *ops = NULL;
 
-       mutex_lock(&tm6000_extension_devlist_lock);
+       mutex_lock(&tm6000_devlist_mutex);
        if (!list_empty(&tm6000_extension_devlist)) {
                list_for_each_entry(ops, &tm6000_extension_devlist, next) {
                        if (ops->init)
                                ops->init(dev);
                }
        }
-       mutex_unlock(&tm6000_extension_devlist_lock);
+       mutex_unlock(&tm6000_devlist_mutex);
 }
 
 void tm6000_close_extension(struct tm6000_core *dev)
 {
        struct tm6000_ops *ops = NULL;
 
-       mutex_lock(&tm6000_extension_devlist_lock);
+       mutex_lock(&tm6000_devlist_mutex);
        if (!list_empty(&tm6000_extension_devlist)) {
                list_for_each_entry(ops, &tm6000_extension_devlist, next) {
                        if (ops->fini)
                                ops->fini(dev);
                }
        }
-       mutex_unlock(&tm6000_extension_devlist_lock);
+       mutex_lock(&tm6000_devlist_mutex);
 }