V4L/DVB (12770): Add tm6000 driver to staging tree
Adds a driver for Trident TV Master tm5600/tm6000 chips. Those USB devices are usually found with a Xceive xc2028/xc3028 tuner, although the firmware seems to be modified to work with those chips on some older devices. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
e40152ee1e
commit
9701dc94a1
|
@ -0,0 +1,14 @@
|
|||
config VIDEO_TM6000
|
||||
tristate "TV Master TM5600/6000 driver"
|
||||
select VIDEO_V4L2
|
||||
select TUNER_XC2028
|
||||
select VIDEO_USB_ISOC
|
||||
select VIDEOBUF_VMALLOC
|
||||
help
|
||||
Support for TM5600/TM6000 USB Device
|
||||
|
||||
Since these cards have no MPEG decoder onboard, they transmit
|
||||
only compressed MPEG data over the usb bus, so you need
|
||||
an external software decoder to watch TV on your computer.
|
||||
|
||||
Say Y if you own such a device and want to use it.
|
|
@ -0,0 +1,8 @@
|
|||
tm6000-objs := tm6000-cards.o \
|
||||
tm6000-core.o \
|
||||
tm6000-i2c.o \
|
||||
tm6000-video.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_TM6000) += tm6000.o
|
||||
|
||||
EXTRA_CFLAGS = -Idrivers/media/video
|
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
tm6000-cards.c - driver for TM5600/TM6000 USB video capture devices
|
||||
|
||||
Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
|
||||
|
||||
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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/version.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/tuner.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
|
||||
|
||||
#define TM6000_MAXBOARDS 16
|
||||
static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET };
|
||||
|
||||
module_param_array(card, int, NULL, 0444);
|
||||
|
||||
struct tm6000_board {
|
||||
char *name;
|
||||
|
||||
struct tm6000_capabilities caps;
|
||||
|
||||
int tuner_type; /* type of the tuner */
|
||||
int tuner_addr; /* tuner address */
|
||||
};
|
||||
|
||||
|
||||
struct tm6000_board tm6000_boards[] = {
|
||||
[TM6000_BOARD_UNKNOWN] = {
|
||||
.name = "Unknown tm6000 video grabber",
|
||||
.caps = {
|
||||
.has_tuner = 1,
|
||||
},
|
||||
},
|
||||
[TM5600_BOARD_GENERIC] = {
|
||||
.name = "Generic tm5600 board",
|
||||
.tuner_type = TUNER_XC2028,
|
||||
.tuner_addr = 0xc2,
|
||||
.caps = {
|
||||
.has_tuner = 1,
|
||||
},
|
||||
},
|
||||
[TM6000_BOARD_GENERIC] = {
|
||||
.name = "Generic tm6000 board",
|
||||
.tuner_type = TUNER_XC2028,
|
||||
.tuner_addr = 0xc2,
|
||||
.caps = {
|
||||
.has_tuner = 1,
|
||||
.has_dvb = 1,
|
||||
},
|
||||
},
|
||||
[TM5600_BOARD_10MOONS_UT821] = {
|
||||
.name = "10Moons UT 821",
|
||||
.tuner_type = TUNER_XC2028,
|
||||
.tuner_addr = 0xc2,
|
||||
.caps = {
|
||||
.has_tuner = 1,
|
||||
.has_eeprom = 1,
|
||||
},
|
||||
},
|
||||
[TM6000_BOARD_10MOONS_UT330] = {
|
||||
.name = "10Moons UT 330",
|
||||
.tuner_type = TUNER_XC2028,
|
||||
.tuner_addr = 0xc8,
|
||||
.caps = {
|
||||
.has_tuner = 1,
|
||||
.has_dvb = 1,
|
||||
.has_zl10353 = 1,
|
||||
.has_eeprom = 1,
|
||||
},
|
||||
},
|
||||
[TM6000_BOARD_ADSTECH_DUAL_TV] = {
|
||||
.name = "ADSTECH Dual TV USB",
|
||||
.tuner_type = TUNER_XC2028,
|
||||
.tuner_addr = 0xc8,
|
||||
.caps = {
|
||||
.has_tuner = 1,
|
||||
.has_tda9874 = 1,
|
||||
.has_dvb = 1,
|
||||
.has_zl10353 = 1,
|
||||
.has_eeprom = 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* 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(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int tm6000_init_dev(struct tm6000_core *dev)
|
||||
{
|
||||
struct v4l2_frequency f;
|
||||
int rc = 0;
|
||||
|
||||
mutex_init(&dev->lock);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
/* Initializa board-specific data */
|
||||
dev->tuner_type = tm6000_boards[dev->model].tuner_type;
|
||||
dev->tuner_addr = tm6000_boards[dev->model].tuner_addr;
|
||||
|
||||
dev->caps = tm6000_boards[dev->model].caps;
|
||||
|
||||
/* initialize hardware */
|
||||
rc=tm6000_init (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;
|
||||
|
||||
/* 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 */
|
||||
f.tuner = 0;
|
||||
f.type = V4L2_TUNER_ANALOG_TV;
|
||||
f.frequency = 3092; /* 193.25 MHz */
|
||||
dev->freq = f.frequency;
|
||||
|
||||
tm6000_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
|
||||
|
||||
err:
|
||||
mutex_unlock(&dev->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
|
||||
#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
|
||||
|
||||
static void get_max_endpoint ( struct usb_device *usbdev,
|
||||
char *msgtype,
|
||||
struct usb_host_endpoint *curr_e,
|
||||
unsigned int *maxsize,
|
||||
struct usb_host_endpoint **ep )
|
||||
{
|
||||
u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize);
|
||||
unsigned int size = tmp & 0x7ff;
|
||||
|
||||
if (usbdev->speed == USB_SPEED_HIGH)
|
||||
size = size * hb_mult (tmp);
|
||||
|
||||
if (size>*maxsize) {
|
||||
*ep = curr_e;
|
||||
*maxsize = size;
|
||||
printk("tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n",
|
||||
msgtype, curr_e->desc.bEndpointAddress,
|
||||
size);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tm6000_usb_probe()
|
||||
* checks for supported devices
|
||||
*/
|
||||
static int tm6000_usb_probe(struct usb_interface *interface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *usbdev;
|
||||
struct tm6000_core *dev = NULL;
|
||||
int i,rc=0;
|
||||
int nr=0;
|
||||
char *speed;
|
||||
|
||||
|
||||
usbdev=usb_get_dev(interface_to_usbdev(interface));
|
||||
|
||||
/* Selects the proper interface */
|
||||
rc=usb_set_interface(usbdev,0,1);
|
||||
if (rc<0)
|
||||
goto err;
|
||||
|
||||
/* 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);
|
||||
usb_put_dev(usbdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Create and initialize dev struct */
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (dev == NULL) {
|
||||
printk ("tm6000" ": out of memory!\n");
|
||||
usb_put_dev(usbdev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
spin_lock_init(&dev->slock);
|
||||
|
||||
/* Increment usage count */
|
||||
tm6000_devused|=1<<nr;
|
||||
|
||||
dev->udev= usbdev;
|
||||
dev->model=id->driver_info;
|
||||
snprintf(dev->name, 29, "tm6000 #%d", nr);
|
||||
dev->devno=nr;
|
||||
|
||||
switch (usbdev->speed) {
|
||||
case USB_SPEED_LOW:
|
||||
speed = "1.5";
|
||||
break;
|
||||
case USB_SPEED_UNKNOWN:
|
||||
case USB_SPEED_FULL:
|
||||
speed = "12";
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
speed = "480";
|
||||
break;
|
||||
default:
|
||||
speed = "unknown";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get endpoints */
|
||||
for (i = 0; i < interface->num_altsetting; i++) {
|
||||
int ep;
|
||||
|
||||
for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
|
||||
struct usb_host_endpoint *e;
|
||||
int dir_out;
|
||||
|
||||
e = &interface->altsetting[i].endpoint[ep];
|
||||
|
||||
dir_out = ((e->desc.bEndpointAddress &
|
||||
USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
|
||||
|
||||
printk("tm6000: alt %d, interface %i, class %i\n",
|
||||
i,
|
||||
interface->altsetting[i].desc.bInterfaceNumber,
|
||||
interface->altsetting[i].desc.bInterfaceClass);
|
||||
|
||||
switch (e->desc.bmAttributes) {
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (!dir_out) {
|
||||
get_max_endpoint (usbdev, "Bulk IN", e,
|
||||
&dev->max_bulk_in,
|
||||
&dev->bulk_in);
|
||||
} else {
|
||||
get_max_endpoint (usbdev, "Bulk OUT", e,
|
||||
&dev->max_bulk_out,
|
||||
&dev->bulk_out);
|
||||
}
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
if (!dir_out) {
|
||||
get_max_endpoint (usbdev, "ISOC IN", e,
|
||||
&dev->max_isoc_in,
|
||||
&dev->isoc_in);
|
||||
} else {
|
||||
get_max_endpoint (usbdev, "ISOC OUT", e,
|
||||
&dev->max_isoc_out,
|
||||
&dev->isoc_out);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
le16_to_cpu(dev->udev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->udev->descriptor.idProduct),
|
||||
interface->altsetting->desc.bInterfaceNumber);
|
||||
|
||||
/* check if the the device has the iso in endpoint at the correct place */
|
||||
if (!dev->isoc_in) {
|
||||
printk("tm6000: probing error: no IN ISOC endpoint!\n");
|
||||
rc= -ENODEV;
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* save our data pointer in this interface device */
|
||||
usb_set_intfdata(interface, dev);
|
||||
|
||||
printk("tm6000: Found %s\n", tm6000_boards[dev->model].name);
|
||||
|
||||
rc=tm6000_init_dev(dev);
|
||||
|
||||
if (rc<0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
tm6000_devused&=~(1<<nr);
|
||||
usb_put_dev(usbdev);
|
||||
|
||||
kfree(dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* tm6000_usb_disconnect()
|
||||
* called when the device gets diconencted
|
||||
* video device will be unregistered on v4l2_close in case it is still open
|
||||
*/
|
||||
static void tm6000_usb_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct tm6000_core *dev = usb_get_intfdata(interface);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
tm6000_i2c_unregister(dev);
|
||||
|
||||
printk("tm6000: disconnecting %s\n", dev->name);
|
||||
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
tm6000_i2c_unregister(dev);
|
||||
|
||||
tm6000_v4l2_unregister(dev);
|
||||
|
||||
// wake_up_interruptible_all(&dev->open);
|
||||
|
||||
dev->state |= DEV_DISCONNECTED;
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
static struct usb_driver tm6000_usb_driver = {
|
||||
.name = "tm6000",
|
||||
.probe = tm6000_usb_probe,
|
||||
.disconnect = tm6000_usb_disconnect,
|
||||
.id_table = tm6000_id_table,
|
||||
};
|
||||
|
||||
static int __init tm6000_module_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
printk(KERN_INFO "tm6000" " v4l2 driver version %d.%d.%d loaded\n",
|
||||
(TM6000_VERSION >> 16) & 0xff,
|
||||
(TM6000_VERSION >> 8) & 0xff, TM6000_VERSION & 0xff);
|
||||
|
||||
/* register this driver with the USB subsystem */
|
||||
result = usb_register(&tm6000_usb_driver);
|
||||
if (result)
|
||||
printk("tm6000"
|
||||
" usb_register failed. Error number %d.\n", result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit tm6000_module_exit(void)
|
||||
{
|
||||
/* deregister at USB subsystem */
|
||||
usb_deregister(&tm6000_usb_driver);
|
||||
}
|
||||
|
||||
module_init(tm6000_module_init);
|
||||
module_exit(tm6000_module_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000 USB2 adapter");
|
||||
MODULE_AUTHOR("Mauro Carvalho Chehab");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,633 @@
|
|||
/*
|
||||
tm6000-core.c - driver for TM5600/TM6000 USB video capture devices
|
||||
|
||||
Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
|
||||
|
||||
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 <linux/kernel.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/video_decoder.h>
|
||||
#include "tm6000.h"
|
||||
#include "tm6000-regs.h"
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/tuner.h>
|
||||
|
||||
#ifdef HACK /* HACK */
|
||||
#include "tm6000-hack.c"
|
||||
#endif
|
||||
|
||||
#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)
|
||||
data = kzalloc(len, GFP_KERNEL);
|
||||
|
||||
|
||||
if (req_type & USB_DIR_IN)
|
||||
pipe=usb_rcvctrlpipe(dev->udev, 0);
|
||||
else {
|
||||
pipe=usb_sndctrlpipe(dev->udev, 0);
|
||||
memcpy(data, buf, len);
|
||||
}
|
||||
|
||||
if (tm6000_debug & V4L2_DEBUG_I2C) {
|
||||
if (!ini)
|
||||
last=ini=jiffies;
|
||||
|
||||
printk("%06i (dev %p, pipe %08x): ", n, dev->udev, pipe);
|
||||
|
||||
printk( "%s: %06u ms %06u ms %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(">>> ");
|
||||
for (i=0;i<len;i++) {
|
||||
printk(" %02x",buf[i]);
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
}
|
||||
|
||||
ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, data,
|
||||
len, USB_TIMEOUT);
|
||||
|
||||
if (req_type & USB_DIR_IN)
|
||||
memcpy(buf, data, len);
|
||||
|
||||
if (tm6000_debug & V4L2_DEBUG_I2C) {
|
||||
if (ret<0) {
|
||||
if (req_type & USB_DIR_IN)
|
||||
printk("<<< (len=%d)\n",len);
|
||||
|
||||
printk("%s: Error #%d\n", __FUNCTION__, ret);
|
||||
} else if (req_type & USB_DIR_IN) {
|
||||
printk("<<< ");
|
||||
for (i=0;i<len;i++) {
|
||||
printk(" %02x",buf[i]);
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
}
|
||||
|
||||
kfree(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tm6000_set_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index)
|
||||
{
|
||||
return
|
||||
tm6000_read_write_usb (dev, USB_DIR_OUT | USB_TYPE_VENDOR,
|
||||
req, value, index, NULL, 0);
|
||||
}
|
||||
|
||||
int tm6000_get_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index)
|
||||
{
|
||||
int rc;
|
||||
u8 buf[1];
|
||||
|
||||
rc=tm6000_read_write_usb (dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
|
||||
value, index, buf, 1);
|
||||
|
||||
if (rc<0)
|
||||
return rc;
|
||||
|
||||
return *buf;
|
||||
}
|
||||
|
||||
int tm6000_get_reg16 (struct tm6000_core *dev, u8 req, u16 value, u16 index)
|
||||
{
|
||||
int rc;
|
||||
u8 buf[2];
|
||||
|
||||
rc=tm6000_read_write_usb (dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
|
||||
value, index, buf, 2);
|
||||
|
||||
if (rc<0)
|
||||
return rc;
|
||||
|
||||
return buf[1]|buf[0]<<8;
|
||||
}
|
||||
|
||||
void tm6000_set_fourcc_format(struct tm6000_core *dev)
|
||||
{
|
||||
if (dev->fourcc==V4L2_PIX_FMT_UYVY) {
|
||||
/* Sets driver to UYUV */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xc1, 0xd0);
|
||||
} else {
|
||||
/* Sets driver to YUV2 */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xc1, 0x90);
|
||||
}
|
||||
}
|
||||
|
||||
int tm6000_init_analog_mode (struct tm6000_core *dev)
|
||||
{
|
||||
|
||||
/* Enables soft reset */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x3f, 0x01);
|
||||
|
||||
if (dev->scaler) {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xc0, 0x20);
|
||||
} else {
|
||||
/* Enable Hfilter and disable TS Drop err */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xc0, 0x80);
|
||||
}
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xc3, 0x88);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xda, 0x23);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xd1, 0xc0);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xd2, 0xd8);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xd6, 0x06);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xdf, 0x1f);
|
||||
|
||||
/* AP Software reset */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xff, 0x08);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xff, 0x00);
|
||||
|
||||
tm6000_set_fourcc_format(dev);
|
||||
|
||||
/* Disables soft reset */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x3f, 0x00);
|
||||
|
||||
/* E3: Select input 0 - TV tuner */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe3, 0x00);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, 0x60);
|
||||
|
||||
/* Tuner firmware can now be loaded */
|
||||
|
||||
tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_1, 0x00);
|
||||
msleep(11);
|
||||
|
||||
/* This controls input */
|
||||
tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_2, 0x0);
|
||||
tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_3, 0x01);
|
||||
msleep(20);
|
||||
|
||||
/*FIXME: Hack!!! */
|
||||
struct v4l2_frequency f;
|
||||
mutex_lock(&dev->lock);
|
||||
f.frequency=dev->freq;
|
||||
tm6000_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,&f);
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
msleep(100);
|
||||
tm6000_set_standard (dev, &dev->norm);
|
||||
tm6000_set_audio_bitrate (dev,48000);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* The meaning of those initializations are unknown */
|
||||
u8 init_tab[][2] = {
|
||||
/* REG VALUE */
|
||||
{ 0xdf, 0x1f },
|
||||
{ 0xff, 0x08 },
|
||||
{ 0xff, 0x00 },
|
||||
{ 0xd5, 0x4f },
|
||||
{ 0xda, 0x23 },
|
||||
{ 0xdb, 0x08 },
|
||||
{ 0xe2, 0x00 },
|
||||
{ 0xe3, 0x10 },
|
||||
{ 0xe5, 0x00 },
|
||||
{ 0xe8, 0x00 },
|
||||
{ 0xeb, 0x64 }, /* 48000 bits/sample, external input */
|
||||
{ 0xee, 0xc2 },
|
||||
{ 0x3f, 0x01 }, /* Start of soft reset */
|
||||
{ 0x00, 0x00 },
|
||||
{ 0x01, 0x07 },
|
||||
{ 0x02, 0x5f },
|
||||
{ 0x03, 0x00 },
|
||||
{ 0x05, 0x64 },
|
||||
{ 0x07, 0x01 },
|
||||
{ 0x08, 0x82 },
|
||||
{ 0x09, 0x36 },
|
||||
{ 0x0a, 0x50 },
|
||||
{ 0x0c, 0x6a },
|
||||
{ 0x11, 0xc9 },
|
||||
{ 0x12, 0x07 },
|
||||
{ 0x13, 0x3b },
|
||||
{ 0x14, 0x47 },
|
||||
{ 0x15, 0x6f },
|
||||
{ 0x17, 0xcd },
|
||||
{ 0x18, 0x1e },
|
||||
{ 0x19, 0x8b },
|
||||
{ 0x1a, 0xa2 },
|
||||
{ 0x1b, 0xe9 },
|
||||
{ 0x1c, 0x1c },
|
||||
{ 0x1d, 0xcc },
|
||||
{ 0x1e, 0xcc },
|
||||
{ 0x1f, 0xcd },
|
||||
{ 0x20, 0x3c },
|
||||
{ 0x21, 0x3c },
|
||||
{ 0x2d, 0x48 },
|
||||
{ 0x2e, 0x88 },
|
||||
{ 0x30, 0x22 },
|
||||
{ 0x31, 0x61 },
|
||||
{ 0x32, 0x74 },
|
||||
{ 0x33, 0x1c },
|
||||
{ 0x34, 0x74 },
|
||||
{ 0x35, 0x1c },
|
||||
{ 0x36, 0x7a },
|
||||
{ 0x37, 0x26 },
|
||||
{ 0x38, 0x40 },
|
||||
{ 0x39, 0x0a },
|
||||
{ 0x42, 0x55 },
|
||||
{ 0x51, 0x11 },
|
||||
{ 0x55, 0x01 },
|
||||
{ 0x57, 0x02 },
|
||||
{ 0x58, 0x35 },
|
||||
{ 0x59, 0xa0 },
|
||||
{ 0x80, 0x15 },
|
||||
{ 0x82, 0x42 },
|
||||
{ 0xc1, 0xd0 },
|
||||
{ 0xc3, 0x88 },
|
||||
{ 0x3f, 0x00 }, /* End of the soft reset */
|
||||
};
|
||||
|
||||
int tm6000_init (struct tm6000_core *dev)
|
||||
{
|
||||
int board, rc=0, i;
|
||||
|
||||
#ifdef HACK /* HACK */
|
||||
init_tm6000(dev);
|
||||
return 0;
|
||||
#else
|
||||
|
||||
/* Load board's initialization table */
|
||||
for (i=0; i< ARRAY_SIZE(init_tab); i++) {
|
||||
rc= tm6000_set_reg (dev, REQ_07_SET_GET_AVREG,
|
||||
init_tab[i][0],init_tab[i][1]);
|
||||
if (rc<0) {
|
||||
printk (KERN_ERR "Error %i while setting reg %d to value %d\n",
|
||||
rc, init_tab[i][0],init_tab[i][1]);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check board version - maybe 10Moons specific */
|
||||
board=tm6000_get_reg16 (dev, 0x40, 0, 0);
|
||||
if (board >=0) {
|
||||
printk (KERN_INFO "Board version = 0x%04x\n",board);
|
||||
} else {
|
||||
printk (KERN_ERR "Error %i while retrieving board version\n",board);
|
||||
}
|
||||
|
||||
tm6000_set_reg (dev, REQ_05_SET_GET_USBREG, 0x18, 0x00);
|
||||
msleep(5); /* Just to be conservative */
|
||||
|
||||
/* Reset GPIO1. Maybe, this is 10 Moons specific */
|
||||
for (i=0; i< 3; i++) {
|
||||
rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_1, 0);
|
||||
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, TM6000_GPIO_1, 1);
|
||||
if (rc<0) {
|
||||
printk (KERN_ERR "Error %i doing GPIO1 reset\n",rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!i)
|
||||
rc=tm6000_get_reg16(dev, 0x40,0,0);
|
||||
}
|
||||
return 0;
|
||||
|
||||
#endif /* HACK */
|
||||
}
|
||||
|
||||
#define tm6000_wrt(dev,req,reg,val, data...) \
|
||||
{ const static u8 _val[] = data; \
|
||||
tm6000_read_write_usb(dev,USB_DIR_OUT | USB_TYPE_VENDOR, \
|
||||
req,reg, val, (u8 *) _val, ARRAY_SIZE(_val)); \
|
||||
}
|
||||
|
||||
/*
|
||||
TM5600/6000 register values to set video standards.
|
||||
There's an adjust, common to all, for composite video
|
||||
Additional adjustments are required for S-Video, based on std.
|
||||
|
||||
Standards values for TV S-Video Changes
|
||||
REG PAL PAL_M PAL_N SECAM NTSC Comp. PAL PAL_M PAL_N SECAM NTSC
|
||||
0xdf 0x1f 0x1f 0x1f 0x1f 0x1f
|
||||
0xe2 0x00 0x00 0x00 0x00 0x00
|
||||
0xe8 0x0f 0x0f 0x0f 0x0f 0x0f 0x00 0x00 0x00 0x00 0x00
|
||||
0xeb 0x60 0x60 0x60 0x60 0x60 0x64 0x64 0x64 0x64 0x64 0x64
|
||||
0xd5 0x5f 0x5f 0x5f 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f
|
||||
0xe3 0x00 0x00 0x00 0x00 0x00 0x10 0x10 0x10 0x10 0x10 0x10
|
||||
0xe5 0x00 0x00 0x00 0x00 0x00 0x10 0x10 0x10 0x10 0x10
|
||||
0x3f 0x01 0x01 0x01 0x01 0x01
|
||||
0x00 0x32 0x04 0x36 0x38 0x00 0x33 0x05 0x37 0x39 0x01
|
||||
0x01 0x0e 0x0e 0x0e 0x0e 0x0f
|
||||
0x02 0x5f 0x5f 0x5f 0x5f 0x5f
|
||||
0x03 0x02 0x00 0x02 0x02 0x00 0x04 0x04 0x04 0x03 0x03
|
||||
0x07 0x01 0x01 0x01 0x01 0x01 0x00 0x00
|
||||
0x17 0xcd 0xcd 0xcd 0xcd 0xcd 0x8b
|
||||
0x18 0x25 0x1e 0x1e 0x24 0x1e
|
||||
0x19 0xd5 0x83 0x91 0x92 0x8b
|
||||
0x1a 0x63 0x0a 0x1f 0xe8 0xa2
|
||||
0x1b 0x50 0xe0 0x0c 0xed 0xe9
|
||||
0x1c 0x1c 0x1c 0x1c 0x1c 0x1c
|
||||
0x1d 0xcc 0xcc 0xcc 0xcc 0xcc
|
||||
0x1e 0xcc 0xcc 0xcc 0xcc 0xcc
|
||||
0x1f 0xcd 0xcd 0xcd 0xcd 0xcd
|
||||
0x2e 0x8c 0x88 0x8c 0x8c 0x88 0x88
|
||||
0x30 0x2c 0x20 0x2c 0x2c 0x22 0x2a 0x22 0x22 0x2a
|
||||
0x31 0xc1 0x61 0xc1 0xc1 0x61
|
||||
0x33 0x0c 0x0c 0x0c 0x2c 0x1c
|
||||
0x35 0x1c 0x1c 0x1c 0x18 0x1c
|
||||
0x82 0x52 0x52 0x52 0x42 0x42
|
||||
0x04 0xdc 0xdc 0xdc 0xdd
|
||||
0x0d 0x07 0x07 0x07 0x87 0x07
|
||||
0x3f 0x00 0x00 0x00 0x00 0x00
|
||||
*/
|
||||
|
||||
int tm6000_set_standard (struct tm6000_core *dev, v4l2_std_id *norm)
|
||||
{
|
||||
dev->norm=*norm;
|
||||
|
||||
/* HACK: Should use, instead, the common code!!! */
|
||||
if (*norm & V4L2_STD_PAL_M) {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xdf, 0x1f);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe2, 0x00);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe8, 0x0f);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, 0x60);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xd5, 0x5f);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe3, 0x00);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe5, 0x00);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x3f, 0x01);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x04);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x01, 0x0e);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x02, 0x5f);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x00);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x07, 0x01);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x18, 0x1e);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x19, 0x83);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1a, 0x0a);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1b, 0xe0);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1c, 0x1c);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1d, 0xcc);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1e, 0xcc);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1f, 0xcd);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x2e, 0x88);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x20);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x31, 0x61);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x33, 0x0c);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x35, 0x1c);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x82, 0x52);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x04, 0xdc);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x0d, 0x07);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x3f, 0x00);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* */
|
||||
// tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x01);
|
||||
// tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00);
|
||||
|
||||
/* Set registers common to all standards */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xdf, 0x1f);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe2, 0x00);
|
||||
|
||||
switch (dev->input) {
|
||||
case TM6000_INPUT_TV:
|
||||
/* Seems to disable ADC2 - needed for TV and RCA */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe8, 0x0f);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, 0x60);
|
||||
|
||||
if (*norm & V4L2_STD_PAL) {
|
||||
/* Enable UV_FLT_EN */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xd5, 0x5f);
|
||||
} else {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xd5, 0x4f);
|
||||
}
|
||||
|
||||
/* E3: Select input 0 */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe3, 0x00);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe5, 0x10);
|
||||
|
||||
break;
|
||||
case TM6000_INPUT_COMPOSITE:
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, 0x64);
|
||||
/* E3: Select input 1 */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe3, 0x10);
|
||||
break;
|
||||
case TM6000_INPUT_SVIDEO:
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe8, 0x00);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, 0x64);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xd5, 0x4f);
|
||||
/* E3: Select input 1 */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe3, 0x10);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xe5, 0x10);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* Software reset */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x3f, 0x01);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x02, 0x5f);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x07, 0x01);
|
||||
// tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x17, 0xcd);
|
||||
|
||||
/* Horizontal Sync DTO = 0x1ccccccd */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1c, 0x1c);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1d, 0xcc);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1e, 0xcc);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1f, 0xcd);
|
||||
|
||||
/* Vertical Height */
|
||||
if (*norm & V4L2_STD_525_60) {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x31, 0x61);
|
||||
} else {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x31, 0xc1);
|
||||
}
|
||||
|
||||
/* Horizontal Length */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x2f, 640/8);
|
||||
|
||||
if (*norm & V4L2_STD_PAL) {
|
||||
/* Common to All PAL Standards */
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x01, 0x0e);
|
||||
|
||||
/* Vsync Hsinc Lockout End */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x33, 0x0c);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x35, 0x1c);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x82, 0x52);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x04, 0xdc);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x0d, 0x07);
|
||||
if (*norm & V4L2_STD_PAL_M) {
|
||||
|
||||
/* Chroma DTO */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x18, 0x1e);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x19, 0x83);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1a, 0x0a);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1b, 0xe0);
|
||||
|
||||
/* Active Video Horiz Start Time */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x2e, 0x88);
|
||||
|
||||
if (dev->input==TM6000_INPUT_SVIDEO) {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x05);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x04);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x22);
|
||||
} else {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x04);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x00);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x20);
|
||||
}
|
||||
} else if (*norm & V4L2_STD_PAL_N) {
|
||||
/* Chroma DTO */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x18, 0x1e);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x19, 0x91);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1a, 0x1f);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1b, 0x0c);
|
||||
|
||||
if (dev->input==TM6000_INPUT_SVIDEO) {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x37);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x04);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x2e, 0x88);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x22);
|
||||
} else {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x36);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x02);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x2e, 0x8c);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x2c);
|
||||
}
|
||||
} else { // Other PAL standards
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x18, 0x25);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x19, 0xd5);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1a, 0x63);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1b, 0x50);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x2e, 0x8c);
|
||||
|
||||
if (dev->input==TM6000_INPUT_SVIDEO) {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x33);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x04);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x2a);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x2c);
|
||||
} else {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x32);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x02);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x2c);
|
||||
}
|
||||
}
|
||||
} if (*norm & V4L2_STD_SECAM) {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x01, 0x0e);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x18, 0x24);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x19, 0x92);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1a, 0xe8);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1b, 0xed);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x2e, 0x8c);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x33, 0x2c);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x35, 0x18);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x82, 0x42);
|
||||
// Register 0x04 is not initialized on SECAM
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x0d, 0x87);
|
||||
|
||||
if (dev->input==TM6000_INPUT_SVIDEO) {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x39);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x03);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x2a);
|
||||
} else {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x38);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x02);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x2c);
|
||||
}
|
||||
} else { /* NTSC */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x01, 0x0f);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x18, 0x1e);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x19, 0x8b);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1a, 0xa2);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x1b, 0xe9);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x2e, 0x88);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x30, 0x22);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x33, 0x1c);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x35, 0x1c);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x82, 0x42);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x0d, 0x07);
|
||||
if (dev->input==TM6000_INPUT_SVIDEO) {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x01);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x03);
|
||||
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x07, 0x00);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x17, 0x8b);
|
||||
} else {
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00, 0x00);
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x03, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* End of software reset */
|
||||
tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x3f, 0x00);
|
||||
|
||||
msleep(40);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tm6000_set_audio_bitrate (struct tm6000_core *dev, int bitrate)
|
||||
{
|
||||
int val;
|
||||
|
||||
val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, 0x0);
|
||||
printk("Original value=%d\n",val);
|
||||
if (val<0)
|
||||
return val;
|
||||
|
||||
val &= 0x0f; /* Preserve the audio input control bits */
|
||||
switch (bitrate) {
|
||||
case 44100:
|
||||
val|=0xd0;
|
||||
break;
|
||||
case 48000:
|
||||
val|=0x60;
|
||||
break;
|
||||
}
|
||||
val=tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, val);
|
||||
|
||||
return val;
|
||||
}
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
tm6000-i2c.c - driver for TM5600/TM6000 USB video capture devices
|
||||
|
||||
Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
|
||||
|
||||
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 <linux/kernel.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include "tm6000.h"
|
||||
#include "tm6000-regs.h"
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/tuner.h>
|
||||
#include "tuner-xc2028.h"
|
||||
|
||||
|
||||
/*FIXME: Hack to avoid needing to patch i2c-id.h */
|
||||
#define I2C_HW_B_TM6000 I2C_HW_B_EM28XX
|
||||
/* ----------------------------------------------------------- */
|
||||
|
||||
static unsigned int i2c_scan = 0;
|
||||
module_param(i2c_scan, int, 0444);
|
||||
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
|
||||
|
||||
static unsigned int i2c_debug = 0;
|
||||
module_param(i2c_debug, int, 0644);
|
||||
MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
|
||||
|
||||
#define i2c_dprintk(lvl,fmt, args...) if (i2c_debug>=lvl) do{ \
|
||||
printk(KERN_DEBUG "%s at %s: " fmt, \
|
||||
dev->name, __FUNCTION__ , ##args); } while (0)
|
||||
|
||||
|
||||
/* Returns 0 if address is found */
|
||||
static int tm6000_i2c_scan(struct i2c_adapter *i2c_adap, int addr)
|
||||
{
|
||||
struct tm6000_core *dev = i2c_adap->algo_data;
|
||||
|
||||
#if 1
|
||||
/* HACK: i2c scan is not working yet */
|
||||
if (
|
||||
(dev->caps.has_tuner && (addr==dev->tuner_addr)) ||
|
||||
(dev->caps.has_tda9874 && (addr==0xb0)) ||
|
||||
(dev->caps.has_zl10353 && (addr==0x1e)) ||
|
||||
(dev->caps.has_eeprom && (addr==0xa0))
|
||||
) {
|
||||
printk("Hack: enabling device at addr 0x%02x\n",addr);
|
||||
return (1);
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
#else
|
||||
int rc=-ENODEV;
|
||||
char buf[1];
|
||||
|
||||
/* This sends addr + 1 byte with 0 */
|
||||
rc = tm6000_read_write_usb (dev,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR,
|
||||
REQ_16_SET_GET_I2CSEQ,
|
||||
addr, 0,
|
||||
buf, 0);
|
||||
msleep(10);
|
||||
|
||||
if (rc<0) {
|
||||
if (i2c_debug>=2)
|
||||
printk("no device at addr 0x%02x\n",addr);
|
||||
}
|
||||
|
||||
printk("Hack: check on addr 0x%02x returned %d\n",addr,rc);
|
||||
|
||||
return rc;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
struct i2c_msg msgs[], int num)
|
||||
{
|
||||
struct tm6000_core *dev = i2c_adap->algo_data;
|
||||
int addr, rc, i, byte;
|
||||
|
||||
if (num <= 0)
|
||||
return 0;
|
||||
for (i = 0; i < num; i++) {
|
||||
addr = (msgs[i].addr << 1) &0xff;
|
||||
i2c_dprintk(2,"%s %s addr=0x%x len=%d:",
|
||||
(msgs[i].flags & I2C_M_RD) ? "read" : "write",
|
||||
i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
|
||||
|
||||
if (!msgs[i].len) {
|
||||
/* Do I2C scan */
|
||||
rc=tm6000_i2c_scan(i2c_adap, addr);
|
||||
} else if (msgs[i].flags & I2C_M_RD) {
|
||||
char buf[msgs[i].len];
|
||||
memcpy(buf,msgs[i].buf, msgs[i].len-1);
|
||||
buf[msgs[i].len-1]=0;
|
||||
|
||||
/* Read bytes */
|
||||
/* I2C is assumed to have always a subaddr at the first byte of the
|
||||
message bus. Also, the first i2c value of the answer is returned
|
||||
out of message data.
|
||||
*/
|
||||
rc = tm6000_read_write_usb (dev,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR,
|
||||
REQ_16_SET_GET_I2CSEQ,
|
||||
addr|(*msgs[i].buf)<<8, 0,
|
||||
msgs[i].buf, msgs[i].len);
|
||||
if (i2c_debug>=2) {
|
||||
for (byte = 0; byte < msgs[i].len; byte++) {
|
||||
printk(" %02x", msgs[i].buf[byte]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* write bytes */
|
||||
if (i2c_debug>=2) {
|
||||
for (byte = 0; byte < msgs[i].len; byte++)
|
||||
printk(" %02x", msgs[i].buf[byte]);
|
||||
}
|
||||
|
||||
rc = tm6000_read_write_usb (dev,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
||||
REQ_16_SET_GET_I2CSEQ,
|
||||
addr|(*msgs[i].buf)<<8, 0,
|
||||
msgs[i].buf+1, msgs[i].len-1);
|
||||
}
|
||||
if (i2c_debug>=2)
|
||||
printk("\n");
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return num;
|
||||
err:
|
||||
i2c_dprintk(2," ERROR: %i\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int tm6000_i2c_eeprom( struct tm6000_core *dev,
|
||||
unsigned char *eedata, int len )
|
||||
{
|
||||
int i, rc;
|
||||
unsigned char *p = eedata;
|
||||
unsigned char bytes[17];
|
||||
|
||||
dev->i2c_client.addr = 0xa0 >> 1;
|
||||
|
||||
//006779: OUT: 000006 ms 089867 ms c0 0e a0 00 00 00 01 00 <<< 00
|
||||
//006780: OUT: 000005 ms 089873 ms c0 10 a0 00 00 00 01 00 <<< 00
|
||||
//006781: OUT: 000108 ms 089878 ms 40 0e a0 00 00 00 01 00 >>> 99
|
||||
//006782: OUT: 000015 ms 089986 ms c0 0e a0 00 01 00 01 00 <<< 99
|
||||
//006783: OUT: 000004 ms 090001 ms c0 0e a0 00 10 00 01 00 <<< 99
|
||||
//006784: OUT: 000005 ms 090005 ms 40 10 a0 00 00 00 01 00 >>> 00
|
||||
//006785: OUT: 000308 ms 090010 ms 40 0e a0 00 00 00 01 00 >>> 00
|
||||
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
bytes[0x14+i] = 0;
|
||||
|
||||
rc = i2c_master_recv(&dev->i2c_client, p, 1);
|
||||
if (rc<1) {
|
||||
if (p==eedata) {
|
||||
printk (KERN_WARNING "%s doesn't have eeprom",
|
||||
dev->name);
|
||||
} else {
|
||||
printk(KERN_WARNING
|
||||
"%s: i2c eeprom read error (err=%d)\n",
|
||||
dev->name, rc);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
p++;
|
||||
if (0 == (i % 16))
|
||||
printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i);
|
||||
printk(" %02x", eedata[i]);
|
||||
if ((eedata[i]>=' ')&&(eedata[i]<='z')) {
|
||||
bytes[i%16]=eedata[i];
|
||||
} else {
|
||||
bytes[i%16]='.';
|
||||
}
|
||||
if (15 == (i % 16)) {
|
||||
bytes[i%16]='\0';
|
||||
printk(" %s\n", bytes);
|
||||
}
|
||||
}
|
||||
if ((i%16)!=15) {
|
||||
bytes[i%16]='\0';
|
||||
printk(" %s\n", bytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* algo_control()
|
||||
*/
|
||||
static int algo_control(struct i2c_adapter *adapter,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* functionality()
|
||||
*/
|
||||
static u32 functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
#ifndef I2C_PEC
|
||||
static void inc_use(struct i2c_adapter *adap)
|
||||
{
|
||||
MOD_INC_USE_COUNT;
|
||||
}
|
||||
|
||||
static void dec_use(struct i2c_adapter *adap)
|
||||
{
|
||||
MOD_DEC_USE_COUNT;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define mass_write(addr, reg, data...) \
|
||||
{ const static u8 _val[] = data; \
|
||||
rc=tm6000_read_write_usb(dev,USB_DIR_OUT | USB_TYPE_VENDOR, \
|
||||
REQ_16_SET_GET_I2CSEQ,(reg<<8)+addr, 0x00, (u8 *) _val, \
|
||||
ARRAY_SIZE(_val)); \
|
||||
if (rc<0) { \
|
||||
printk(KERN_ERR "Error on line %d: %d\n",__LINE__,rc); \
|
||||
return rc; \
|
||||
} \
|
||||
msleep (10); \
|
||||
}
|
||||
|
||||
int static init_zl10353 (struct tm6000_core *dev, u8 addr)
|
||||
{
|
||||
int rc=0;
|
||||
|
||||
mass_write (addr, 0x89, { 0x38 });
|
||||
mass_write (addr, 0x8a, { 0x2d });
|
||||
mass_write (addr, 0x50, { 0xff });
|
||||
mass_write (addr, 0x51, { 0x00 , 0x00 , 0x50 });
|
||||
mass_write (addr, 0x54, { 0x72 , 0x49 });
|
||||
mass_write (addr, 0x87, { 0x0e , 0x0e });
|
||||
mass_write (addr, 0x7b, { 0x04 });
|
||||
mass_write (addr, 0x57, { 0xb8 , 0xc2 });
|
||||
mass_write (addr, 0x59, { 0x00 , 0x02 , 0x00 , 0x00 , 0x01 });
|
||||
mass_write (addr, 0x59, { 0x00 , 0x00 , 0xb3 , 0xd0 , 0x01 });
|
||||
mass_write (addr, 0x58, { 0xc0 , 0x11 , 0xc5 , 0xc2 , 0xa4 , 0x01 });
|
||||
mass_write (addr, 0x5e, { 0x01 });
|
||||
mass_write (addr, 0x67, { 0x1c , 0x20 });
|
||||
mass_write (addr, 0x75, { 0x33 });
|
||||
mass_write (addr, 0x85, { 0x10 , 0x40 });
|
||||
mass_write (addr, 0x8c, { 0x0b , 0x00 , 0x40 , 0x00 });
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Tuner callback to provide the proper gpio changes needed for xc2028 */
|
||||
|
||||
static int tm6000_tuner_callback(void *ptr, 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,
|
||||
TM6000_GPIO_1, 0x00);
|
||||
msleep(10);
|
||||
tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
|
||||
TM6000_GPIO_1, 0x01);
|
||||
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(10);
|
||||
rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN,
|
||||
TM6000_GPIO_CLK, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (rc);
|
||||
}
|
||||
|
||||
static int attach_inform(struct i2c_client *client)
|
||||
{
|
||||
struct tm6000_core *dev = client->adapter->algo_data;
|
||||
struct tuner_setup tun_setup;
|
||||
unsigned char eedata[11];
|
||||
|
||||
i2c_dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
|
||||
client->driver->driver.name, client->addr, client->name);
|
||||
|
||||
switch (client->addr<<1) {
|
||||
case 0x1e:
|
||||
init_zl10353 (dev, client->addr);
|
||||
return 0;
|
||||
case 0xa0:
|
||||
tm6000_i2c_eeprom(dev, eedata, sizeof(eedata)-1);
|
||||
eedata[sizeof(eedata)]='\0';
|
||||
|
||||
printk("Board string ID = %s\n",eedata);
|
||||
return 0;
|
||||
case 0xb0:
|
||||
request_module("tvaudio");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If tuner, initialize the tuner part */
|
||||
if ( dev->tuner_addr != client->addr<<1 ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset (&tun_setup, 0, sizeof(tun_setup));
|
||||
|
||||
tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
|
||||
tun_setup.type = dev->tuner_type;
|
||||
tun_setup.addr = dev->tuner_addr>>1;
|
||||
tun_setup.tuner_callback = tm6000_tuner_callback;
|
||||
|
||||
client->driver->command (client,TUNER_SET_TYPE_ADDR, &tun_setup);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_algorithm tm6000_algo = {
|
||||
.master_xfer = tm6000_i2c_xfer,
|
||||
.algo_control = algo_control,
|
||||
.functionality = functionality,
|
||||
};
|
||||
|
||||
static struct i2c_adapter tm6000_adap_template = {
|
||||
#ifdef I2C_PEC
|
||||
.owner = THIS_MODULE,
|
||||
#else
|
||||
.inc_use = inc_use,
|
||||
.dec_use = dec_use,
|
||||
#endif
|
||||
.class = I2C_CLASS_TV_ANALOG,
|
||||
.name = "tm6000",
|
||||
.id = I2C_HW_B_TM6000,
|
||||
.algo = &tm6000_algo,
|
||||
.client_register = attach_inform,
|
||||
};
|
||||
|
||||
static struct i2c_client tm6000_client_template = {
|
||||
.name = "tm6000 internal",
|
||||
};
|
||||
|
||||
/* ----------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* i2c_devs
|
||||
* incomplete list of known devices
|
||||
*/
|
||||
static char *i2c_devs[128] = {
|
||||
[0xc2 >> 1] = "tuner (analog)",
|
||||
};
|
||||
|
||||
/*
|
||||
* do_i2c_scan()
|
||||
* check i2c address range for devices
|
||||
*/
|
||||
static void do_i2c_scan(char *name, struct i2c_client *c)
|
||||
{
|
||||
unsigned char buf;
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < 128; i++) {
|
||||
c->addr = i;
|
||||
rc = i2c_master_recv(c, &buf, 0);
|
||||
if (rc < 0)
|
||||
continue;
|
||||
printk(KERN_INFO "%s: found i2c device @ 0x%x [%s]\n", name,
|
||||
i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tm6000_i2c_call_clients()
|
||||
* send commands to all attached i2c devices
|
||||
*/
|
||||
void tm6000_i2c_call_clients(struct tm6000_core *dev, unsigned int cmd, void *arg)
|
||||
{
|
||||
BUG_ON(NULL == dev->i2c_adap.algo_data);
|
||||
i2c_clients_command(&dev->i2c_adap, cmd, arg);
|
||||
}
|
||||
|
||||
/*
|
||||
* tm6000_i2c_register()
|
||||
* register i2c bus
|
||||
*/
|
||||
int tm6000_i2c_register(struct tm6000_core *dev)
|
||||
{
|
||||
dev->i2c_adap = tm6000_adap_template;
|
||||
dev->i2c_adap.dev.parent = &dev->udev->dev;
|
||||
strcpy(dev->i2c_adap.name, dev->name);
|
||||
dev->i2c_adap.algo_data = dev;
|
||||
i2c_add_adapter(&dev->i2c_adap);
|
||||
|
||||
dev->i2c_client = tm6000_client_template;
|
||||
dev->i2c_client.adapter = &dev->i2c_adap;
|
||||
|
||||
if (i2c_scan)
|
||||
do_i2c_scan(dev->name, &dev->i2c_client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tm6000_i2c_unregister()
|
||||
* unregister i2c_bus
|
||||
*/
|
||||
int tm6000_i2c_unregister(struct tm6000_core *dev)
|
||||
{
|
||||
i2c_del_adapter(&dev->i2c_adap);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
tm6000-regs.h - driver for TM5600/TM6000 USB video capture devices
|
||||
|
||||
Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Define TV Master TM5600/TM6000 Request codes
|
||||
*/
|
||||
#define REQ_00_SET_IR_VALUE 0
|
||||
#define REQ_01_SET_WAKEUP_IRCODE 1
|
||||
#define REQ_02_GET_IR_CODE 2
|
||||
#define REQ_03_SET_GET_MCU_PIN 3
|
||||
#define REQ_04_EN_DISABLE_MCU_INT 4
|
||||
#define REQ_05_SET_GET_USBREG 5
|
||||
/* Write: RegNum, Value, 0 */
|
||||
/* Read : RegNum, Value, 1, RegStatus */
|
||||
#define REQ_06_SET_GET_USBREG_BIT 6
|
||||
#define REQ_07_SET_GET_AVREG 7
|
||||
/* Write: RegNum, Value, 0 */
|
||||
/* Read : RegNum, Value, 1, RegStatus */
|
||||
#define REQ_08_SET_GET_AVREG_BIT 8
|
||||
#define REQ_09_SET_GET_TUNER_FQ 9
|
||||
#define REQ_10_SET_TUNER_SYSTEM 10
|
||||
#define REQ_11_SET_EEPROM_ADDR 11
|
||||
#define REQ_12_SET_GET_EEPROMBYTE 12
|
||||
#define REQ_13_GET_EEPROM_SEQREAD 13
|
||||
#define REQ_14_SET_GET_EEPROM_PAGE 14
|
||||
#define REQ_15_SET_GET_I2CBYTE 15
|
||||
/* Write: Subaddr, Slave Addr, value, 0 */
|
||||
/* Read : Subaddr, Slave Addr, value, 1 */
|
||||
#define REQ_16_SET_GET_I2CSEQ 16
|
||||
/* Subaddr, Slave Addr, 0, length */
|
||||
#define REQ_17_SET_GET_I2CFP 17
|
||||
/* Write: Slave Addr, register, value */
|
||||
/* Read : Slave Addr, register, 2, data */
|
||||
|
||||
/*
|
||||
* Define TV Master TM5600/TM6000 GPIO lines
|
||||
*/
|
||||
|
||||
#define TM6000_GPIO_CLK 0x101
|
||||
#define TM6000_GPIO_DATA 0x100
|
||||
#define TM6000_GPIO_1 0x102
|
||||
#define TM6000_GPIO_2 0x103
|
||||
#define TM6000_GPIO_3 0x104
|
||||
#define TM6000_GPIO_4 0x300
|
||||
#define TM6000_GPIO_5 0x301
|
||||
#define TM6000_GPIO_6 0x304
|
||||
#define TM6000_GPIO_7 0x305
|
||||
|
||||
/*
|
||||
* Define TV Master TM5600/TM6000 URB message codes and length
|
||||
*/
|
||||
|
||||
#define TM6000_URB_MSG_LEN 180
|
||||
enum {
|
||||
TM6000_URB_MSG_VIDEO=1,
|
||||
TM6000_URB_MSG_AUDIO,
|
||||
TM6000_URB_MSG_VBI,
|
||||
TM6000_URB_MSG_PTS,
|
||||
TM6000_URB_MSG_ERR,
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
tm6000-buf.c - driver for TM5600/TM6000 USB video capture devices
|
||||
|
||||
Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
|
||||
|
||||
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/videodev2.h>
|
||||
|
||||
struct usb_isoc_ctl {
|
||||
/* max packet size of isoc transaction */
|
||||
int max_pkt_size;
|
||||
|
||||
/* number of allocated urbs */
|
||||
int num_bufs;
|
||||
|
||||
/* urb for isoc transfers */
|
||||
struct urb **urb;
|
||||
|
||||
/* transfer buffers for isoc transfer */
|
||||
char **transfer_buffer;
|
||||
|
||||
/* Last buffer command and region */
|
||||
u8 cmd;
|
||||
int pos, size, pktsize;
|
||||
|
||||
/* Last field: ODD or EVEN? */
|
||||
int field;
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
tm6000.h - driver for TM5600/TM6000 USB video capture devices
|
||||
|
||||
Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// Use the tm6000-hack, instead of the proper initialization code
|
||||
//#define HACK 1
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/videobuf-vmalloc.h>
|
||||
#include "tm6000-usb-isoc.h"
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define TM6000_VERSION KERNEL_VERSION(0, 0, 1)
|
||||
|
||||
/* Inputs */
|
||||
#define TM6000_INPUT_TV 0
|
||||
#define TM6000_INPUT_COMPOSITE 1
|
||||
#define TM6000_INPUT_SVIDEO 2
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
Basic structures
|
||||
------------------------------------------------------------------*/
|
||||
|
||||
struct tm6000_fmt {
|
||||
char *name;
|
||||
u32 fourcc; /* v4l2 format id */
|
||||
int depth;
|
||||
};
|
||||
|
||||
/* buffer for one video frame */
|
||||
struct tm6000_buffer {
|
||||
/* common v4l buffer stuff -- must be first */
|
||||
struct videobuf_buffer vb;
|
||||
|
||||
struct tm6000_fmt *fmt;
|
||||
};
|
||||
|
||||
struct tm6000_dmaqueue {
|
||||
struct list_head active;
|
||||
struct list_head queued;
|
||||
struct timer_list timeout;
|
||||
|
||||
/* thread for generating video stream*/
|
||||
struct task_struct *kthread;
|
||||
wait_queue_head_t wq;
|
||||
/* Counters to control fps rate */
|
||||
int frame;
|
||||
int ini_jiffies;
|
||||
};
|
||||
|
||||
/* device states */
|
||||
enum tm6000_core_state {
|
||||
DEV_INITIALIZED = 0x01,
|
||||
DEV_DISCONNECTED = 0x02,
|
||||
DEV_MISCONFIGURED = 0x04,
|
||||
};
|
||||
|
||||
/* io methods */
|
||||
enum tm6000_io_method {
|
||||
IO_NONE,
|
||||
IO_READ,
|
||||
IO_MMAP,
|
||||
};
|
||||
|
||||
enum tm6000_mode {
|
||||
TM6000_MODE_UNKNOWN=0,
|
||||
TM6000_MODE_ANALOG,
|
||||
TM6000_MODE_DIGITAL,
|
||||
};
|
||||
|
||||
struct tm6000_capabilities {
|
||||
unsigned int has_tuner:1;
|
||||
unsigned int has_tda9874:1;
|
||||
unsigned int has_dvb:1;
|
||||
unsigned int has_zl10353:1;
|
||||
unsigned int has_eeprom:1;
|
||||
};
|
||||
|
||||
struct tm6000_core {
|
||||
/* generic device properties */
|
||||
char name[30]; /* name (including minor) of the device */
|
||||
int model; /* index in the device_data struct */
|
||||
int devno; /* marks the number of this device */
|
||||
v4l2_std_id norm; /* Current norm */
|
||||
|
||||
enum tm6000_core_state state;
|
||||
|
||||
/* Device Capabilities*/
|
||||
struct tm6000_capabilities caps;
|
||||
|
||||
/* Tuner configuration */
|
||||
int tuner_type; /* type of the tuner */
|
||||
int tuner_addr; /* tuner address */
|
||||
|
||||
/* i2c i/o */
|
||||
struct i2c_adapter i2c_adap;
|
||||
struct i2c_client i2c_client;
|
||||
|
||||
/* video for linux */
|
||||
struct list_head tm6000_corelist;
|
||||
int users;
|
||||
|
||||
/* various device info */
|
||||
unsigned int resources;
|
||||
struct video_device vfd;
|
||||
struct tm6000_dmaqueue vidq;
|
||||
|
||||
int input;
|
||||
int freq;
|
||||
unsigned int fourcc;
|
||||
|
||||
enum tm6000_mode mode;
|
||||
|
||||
/* locks */
|
||||
struct mutex lock;
|
||||
|
||||
/* usb transfer */
|
||||
struct usb_device *udev; /* the usb device */
|
||||
|
||||
struct usb_host_endpoint *bulk_in, *bulk_out, *isoc_in, *isoc_out;
|
||||
unsigned int max_bulk_in, max_bulk_out;
|
||||
unsigned int max_isoc_in, max_isoc_out;
|
||||
|
||||
/* scaler!=0 if scaler is active*/
|
||||
int scaler;
|
||||
|
||||
/* Isoc control struct */
|
||||
struct usb_isoc_ctl isoc_ctl;
|
||||
|
||||
spinlock_t slock;
|
||||
};
|
||||
|
||||
struct tm6000_fh {
|
||||
struct tm6000_core *dev;
|
||||
|
||||
/* video capture */
|
||||
struct tm6000_fmt *fmt;
|
||||
unsigned int width,height;
|
||||
struct videobuf_queue vb_vidq;
|
||||
|
||||
enum v4l2_buf_type type;
|
||||
};
|
||||
|
||||
#define TM6000_STD V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc| \
|
||||
V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \
|
||||
V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM
|
||||
|
||||
/* In tm6000-core.c */
|
||||
extern unsigned long tm6000_devused;
|
||||
|
||||
int tm6000_read_write_usb (struct tm6000_core *dev, u8 reqtype, u8 req,
|
||||
u16 value, u16 index, u8 *buf, u16 len);
|
||||
int tm6000_get_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index);
|
||||
int tm6000_set_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index);
|
||||
int tm6000_init (struct tm6000_core *dev);
|
||||
int tm6000_init_after_firmware (struct tm6000_core *dev);
|
||||
|
||||
int tm6000_init_analog_mode (struct tm6000_core *dev);
|
||||
int tm6000_set_standard (struct tm6000_core *dev, v4l2_std_id *norm);
|
||||
int tm6000_set_audio_bitrate (struct tm6000_core *dev, int bitrate);
|
||||
|
||||
int tm6000_v4l2_register(struct tm6000_core *dev);
|
||||
int tm6000_v4l2_unregister(struct tm6000_core *dev);
|
||||
int tm6000_v4l2_exit(void);
|
||||
void tm6000_set_fourcc_format(struct tm6000_core *dev);
|
||||
|
||||
/* In tm6000-i2c.c */
|
||||
int tm6000_i2c_register(struct tm6000_core *dev);
|
||||
int tm6000_i2c_unregister(struct tm6000_core *dev);
|
||||
void tm6000_i2c_call_clients(struct tm6000_core *dev, unsigned int cmd,
|
||||
void *arg);
|
||||
|
||||
/* In tm6000-queue.c */
|
||||
|
||||
int tm6000_v4l2_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
|
||||
int tm6000_vidioc_streamon(struct file *file, void *priv,
|
||||
enum v4l2_buf_type i);
|
||||
int tm6000_vidioc_streamoff(struct file *file, void *priv,
|
||||
enum v4l2_buf_type i);
|
||||
int tm6000_vidioc_reqbufs (struct file *file, void *priv,
|
||||
struct v4l2_requestbuffers *rb);
|
||||
int tm6000_vidioc_querybuf (struct file *file, void *priv,
|
||||
struct v4l2_buffer *b);
|
||||
int tm6000_vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *b);
|
||||
int tm6000_vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *b);
|
||||
ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count,
|
||||
loff_t * f_pos);
|
||||
unsigned int tm6000_v4l2_poll(struct file *file,
|
||||
struct poll_table_struct *wait);
|
||||
int tm6000_queue_init(struct tm6000_core *dev);
|
||||
|
||||
/* Debug stuff */
|
||||
|
||||
extern int tm6000_debug;
|
||||
|
||||
#define dprintk(dev, level, fmt, arg...) do {\
|
||||
if (tm6000_debug & level) \
|
||||
printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \
|
||||
dev->name, __FUNCTION__ , ##arg); } while (0)
|
||||
|
||||
#define V4L2_DEBUG_REG 0x0004
|
||||
#define V4L2_DEBUG_I2C 0x0008
|
||||
#define V4L2_DEBUG_QUEUE 0x0010
|
||||
#define V4L2_DEBUG_ISOC 0x0020
|
||||
#define V4L2_DEBUG_RES_LOCK 0x0040 /* Resource locking */
|
||||
#define V4L2_DEBUG_OPEN 0x0080 /* video open/close debug */
|
||||
|
||||
#define tm6000_err(fmt, arg...) do {\
|
||||
printk(KERN_ERR "tm6000 %s :"fmt, \
|
||||
__FUNCTION__ , ##arg); } while (0)
|
||||
|
||||
|
|
@ -369,6 +369,7 @@ struct v4l2_pix_format {
|
|||
#define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */
|
||||
#define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */
|
||||
#define V4L2_PIX_FMT_STV0680 v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */
|
||||
#define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */
|
||||
|
||||
/*
|
||||
* F O R M A T E N U M E R A T I O N
|
||||
|
|
Loading…
Reference in New Issue