V4L/DVB (12788): tm6000: Add initial DVB-T support
authorMichel Ludwig <michel.ludwig@gmail.com>
Tue, 21 Aug 2007 20:37:22 +0000 (17:37 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 18 May 2010 03:39:37 +0000 (00:39 -0300)
Signed-off-by: Michel Ludwig <michel.ludwig@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/staging/tm6000/Makefile
drivers/staging/tm6000/hack.c [new file with mode: 0644]
drivers/staging/tm6000/hack.h [new file with mode: 0644]
drivers/staging/tm6000/tm6000-cards.c
drivers/staging/tm6000/tm6000-core.c
drivers/staging/tm6000/tm6000-dvb.c [new file with mode: 0644]
drivers/staging/tm6000/tm6000.h

index 1efc583..1fefe05 100644 (file)
@@ -1,7 +1,10 @@
 tm6000-objs := tm6000-cards.o \
                   tm6000-core.o  \
                   tm6000-i2c.o   \
-                  tm6000-video.o
+                  tm6000-video.o \
+                  tm6000-dvb.o \
+                  hack.o \
+
 
 obj-$(CONFIG_VIDEO_TM6000) += tm6000.o
 
diff --git a/drivers/staging/tm6000/hack.c b/drivers/staging/tm6000/hack.c
new file mode 100644 (file)
index 0000000..87f3f49
--- /dev/null
@@ -0,0 +1,251 @@
+
+
+
+
+
+
+/*
+   hack.h - hackish code that needs to be improved (or removed) at a
+           later point
+
+   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+
+   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 "hack.h"
+
+#include "tm6000.h"
+
+#include <linux/usb.h>
+
+static inline int tm6000_snd_control_msg(struct tm6000_core *dev, __u8 request, __u16 value, __u16 index, void *data, __u16 size)
+{
+       return tm6000_read_write_usb (dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, request, value, index, data, size);
+}
+
+static int pseudo_zl10353_pll(struct tm6000_core *tm6000_dev, struct dvb_frontend_parameters *p)
+{
+       int ret;
+       u8 *data = kzalloc(50*sizeof(u8), GFP_KERNEL);
+
+printk(KERN_ALERT "should set frequency %u\n", p->frequency);
+printk(KERN_ALERT "and bandwith %u\n", p->u.ofdm.bandwidth);
+
+       if(tm6000_dev->dvb->frontend->ops.tuner_ops.set_params) {
+               tm6000_dev->dvb->frontend->ops.tuner_ops.set_params(tm6000_dev->dvb->frontend, p);
+       }
+       else {
+               printk(KERN_ALERT "pseudo zl10353: couldn't set tuner parameters\n");
+       }
+
+       // init ZL10353
+       data[0] = 0x0b;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x501e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x80;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x551e, 0x00, data, 0x1);
+       msleep(100);
+       data[0] = 0x01;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0xea1e, 0x00, data, 0x1);
+       msleep(100);
+       data[0] = 0x00;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0xea1e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x1c;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x561e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x40;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x5e1e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x36;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x641e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x67;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x651e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0xe5;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x661e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x19;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6c1e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0xe9;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6d1e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x44;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x511e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x46;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x521e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x15;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x531e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x0f;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x541e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x75;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x5c1e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x01;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1);
+       msleep(15);
+       data[0] = 0x00;
+       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1);
+       msleep(15);
+
+       msleep(50);
+
+       switch(p->u.ofdm.bandwidth) {
+               case BANDWIDTH_8_MHZ:
+                       data[0] = 0x00;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x36;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x641e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x67;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x651e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0xe5;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x661e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x19;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6c1e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0xe9;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6d1e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x44;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x511e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x46;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x521e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x15;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x531e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x0f;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x541e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x75;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x5c1e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x01;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1);
+                       msleep(15);
+               break;
+
+               default:
+                       printk(KERN_ALERT "tm6000: bandwidth not supported\n");
+               case BANDWIDTH_7_MHZ:
+                       data[0] = 0x00;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x35;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x641e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x5a;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x651e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0xe9;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x661e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x19;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6c1e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0xe9;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6d1e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x44;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x511e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x46;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x521e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x15;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x531e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x0f;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x541e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x86;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x5c1e, 0x00, data, 0x1);
+                       msleep(15);
+                       data[0] = 0x01;
+                       ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1);
+                       msleep(15);
+               break;
+       }
+
+       kfree(data);
+
+       return 0;
+};
+
+
+
+int pseudo_zl10353_set_frontend(struct dvb_frontend *fe,
+                                 struct dvb_frontend_parameters *p)
+{
+       struct tm6000_core *tm6000_dev = fe->dvb->priv;
+       u32 status;
+
+       if(p != NULL) {
+//             mutex_lock(&tm6000_dev->mutex);
+               pseudo_zl10353_pll(tm6000_dev, p);
+//             mutex_unlock(&tm6000_dev->mutex);
+       }
+
+       if(tm6000_dev->dvb->frontend->ops.read_status) {
+               tm6000_dev->dvb->frontend->ops.read_status(tm6000_dev->dvb->frontend, &status);
+               printk(KERN_ALERT "demodulator status: FE_HAS_CARRIER %i \n", (status & FE_HAS_CARRIER));
+               printk(KERN_ALERT "demodulator status: FE_HAS_VITERBI %i \n", (status & FE_HAS_VITERBI));
+               printk(KERN_ALERT "demodulator status: FE_HAS_LOCK %i \n", (status & FE_HAS_LOCK));
+               printk(KERN_ALERT "demodulator status: FE_HAS_SYNC %i \n", (status & FE_HAS_SYNC));
+               printk(KERN_ALERT "demodulator status: FE_HAS_SIGNAL %i \n", (status & FE_HAS_SIGNAL));
+       }
+       else {
+               printk(KERN_ALERT "pseudo zl10353: couldn't read demodulator status\n");
+       }
+       return 0;
+}
+
+int pseudo_zl10353_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+
+       *status = FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK | FE_HAS_SIGNAL;
+
+       return 0;
+}
+
+struct dvb_frontend* pseudo_zl10353_attach(struct tm6000_core *dev,
+                                          const struct zl10353_config *config,
+                                                                  struct i2c_adapter *i2c)
+{
+       struct tm6000_dvb *dvb = dev->dvb;
+
+       dvb->frontend = zl10353_attach(config, i2c);
+       if(!dvb->frontend) {
+               return NULL;
+       }
+
+       /* override some functions with our implementations */
+       dvb->frontend->ops.set_frontend = pseudo_zl10353_set_frontend;
+       dvb->frontend->ops.read_status = pseudo_zl10353_read_status;
+       dvb->frontend->frontend_priv = dev;
+
+       return dvb->frontend;
+}
diff --git a/drivers/staging/tm6000/hack.h b/drivers/staging/tm6000/hack.h
new file mode 100644 (file)
index 0000000..96f1b61
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+   hack.h - hackish code that needs to be improved (or removed) at a
+           later point
+
+   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+
+   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.
+ */
+
+#ifndef HACK_H
+#define HACK_H
+
+#include <linux/i2c.h>
+
+#include "zl10353.h"
+#include "dvb_frontend.h"
+
+struct tm6000_core;
+
+int pseudo_zl103530_init(struct dvb_frontend *fe);
+
+int pseudo_zl10353_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p);
+
+int pseudo_zl10353_read_status(struct dvb_frontend *fe, fe_status_t *status);
+
+int pseudo_zl10353_read_signal_strength(struct dvb_frontend* fe, u16* strength);
+
+int pseudo_zl10353_read_snr(struct dvb_frontend *fe, u16 *snr);
+
+struct dvb_frontend* pseudo_zl10353_attach(struct tm6000_core *dev,
+                                          const struct zl10353_config *config,
+                                                                  struct i2c_adapter *i2c);
+
+#endif
index e8f88ea..b41b3a4 100644 (file)
@@ -185,7 +185,19 @@ static int tm6000_init_dev(struct tm6000_core *dev)
        dev->freq = f.frequency;
 
        tm6000_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f);
-
+       if(dev->caps.has_dvb) {
+               dev->dvb = kzalloc(sizeof(*(dev->dvb)), GFP_KERNEL);
+               if(!dev->dvb) {
+                       rc = -ENOMEM;
+                       goto err;
+               }
+               rc = tm6000_dvb_register(dev);
+               if(rc < 0) {
+                       kfree(dev->dvb);
+                       dev->dvb = NULL;
+                       goto err;
+               }
+       }
 err:
        mutex_unlock(&dev->lock);
        return rc;
@@ -389,6 +401,11 @@ static void tm6000_usb_disconnect(struct usb_interface *interface)
 
        mutex_lock(&dev->lock);
 
+       if(dev->dvb) {
+               tm6000_dvb_unregister(dev);
+               kfree(dev->dvb);
+       }
+
        tm6000_v4l2_unregister(dev);
 
        tm6000_i2c_unregister(dev);
index 6becde2..5e93255 100644 (file)
@@ -3,6 +3,9 @@
 
    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
@@ -207,6 +210,32 @@ int tm6000_init_analog_mode (struct tm6000_core *dev)
        return 0;
 }
 
+int tm6000_init_digital_mode (struct tm6000_core *dev)
+{
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00ff, 0x08);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00ff, 0x00);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x003f, 0x01);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00df, 0x08);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00e2, 0x0c);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00e8, 0xff);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00eb, 0xd8);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00c0, 0x40);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00c1, 0xd0);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00c3, 0x09);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00da, 0x37);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00d1, 0xd8);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00d2, 0xc0);
+       tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00d6, 0x60);
+       msleep(50);
+
+       tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00);
+       tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01);
+       tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00);
+
+       msleep(100);
+
+       return 0;
+}
 
 /* The meaning of those initializations are unknown */
 u8 init_tab[][2] = {
diff --git a/drivers/staging/tm6000/tm6000-dvb.c b/drivers/staging/tm6000/tm6000-dvb.c
new file mode 100644 (file)
index 0000000..d2ec6ca
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+   tm6000-dvb.c - dvb-t support for TM5600/TM6000 USB video capture devices
+
+   Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+
+   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/usb.h>
+
+#include "tm6000.h"
+#include "tm6000-regs.h"
+
+#include "hack.h"
+
+#include "zl10353.h"
+
+#include <media/tuner.h>
+
+static void tm6000_urb_received(struct urb *urb)
+{
+       int ret;
+       struct tm6000_core* dev = urb->context;
+
+       if(urb->status != 0){
+               printk(KERN_ERR "tm6000: status != 0\n");
+       }
+       else if(urb->actual_length>0){
+               dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer,
+                                                  urb->actual_length);
+       }
+
+       if(dev->dvb->streams > 0) {
+               ret = usb_submit_urb(urb, GFP_ATOMIC);
+               if(ret < 0) {
+                       printk(KERN_ERR "tm6000:  error %s\n", __FUNCTION__);
+                       kfree(urb->transfer_buffer);
+                       usb_free_urb(urb);
+               }
+       }
+}
+
+int tm6000_start_stream(struct tm6000_core *dev)
+{
+       int ret;
+       unsigned int pipe, maxPaketSize;
+       struct tm6000_dvb *dvb = dev->dvb;
+
+       printk(KERN_INFO "tm6000: got start stream request %s\n",__FUNCTION__);
+
+       tm6000_init_digital_mode(dev);
+
+//     ret = usb_set_interface(dev->udev, 0, 1);
+//     if (ret<0)
+//             return ret;
+
+/*
+       ret = tm6000_set_led_status(tm6000_dev, 0x1);
+       if(ret < 0) {
+               return -1;
+       }
+*/
+
+       dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if(dvb->bulk_urb == NULL) {
+               printk(KERN_ERR "tm6000: couldn't allocate urb\n");
+               return -ENOMEM;
+       }
+
+       maxPaketSize = dev->bulk_in->desc.wMaxPacketSize;
+
+       dvb->bulk_urb->transfer_buffer = kzalloc(maxPaketSize, GFP_KERNEL);
+       if(dvb->bulk_urb->transfer_buffer == NULL) {
+               usb_free_urb(dvb->bulk_urb);
+               printk(KERN_ERR "tm6000: couldn't allocate transfer buffer!\n");
+               return -ENOMEM;
+       }
+
+       pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in->desc.bEndpointAddress
+                                                         & USB_ENDPOINT_NUMBER_MASK);
+
+       usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe,
+                                                dvb->bulk_urb->transfer_buffer,
+                                                maxPaketSize,
+                                                tm6000_urb_received, dev);
+       ret = usb_clear_halt(dev->udev, pipe);
+       if(ret < 0) {
+               printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n",ret,__FUNCTION__);
+//             return ret;
+       }
+       else {
+               printk(KERN_ERR "tm6000: pipe resetted\n");
+       }
+
+//     mutex_lock(&tm6000_driver.open_close_mutex);
+       ret = usb_submit_urb(dvb->bulk_urb, GFP_KERNEL);
+
+
+//     mutex_unlock(&tm6000_driver.open_close_mutex);
+       if (ret) {
+               printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n",ret);
+
+               kfree(dvb->bulk_urb->transfer_buffer);
+               usb_free_urb(dvb->bulk_urb);
+               return ret;
+       }
+
+       return 0;
+}
+
+void tm6000_stop_stream(struct tm6000_core *dev)
+{
+       struct tm6000_dvb *dvb = dev->dvb;
+
+//     tm6000_set_led_status(tm6000_dev, 0x0);
+
+       if(dvb->bulk_urb) {
+               usb_kill_urb(dvb->bulk_urb);
+               kfree(dvb->bulk_urb->transfer_buffer);
+               usb_free_urb(dvb->bulk_urb);
+               dvb->bulk_urb = NULL;
+       }
+}
+
+int tm6000_start_feed(struct dvb_demux_feed *feed)
+{
+       struct dvb_demux *demux = feed->demux;
+       struct tm6000_core *dev = demux->priv;
+       struct tm6000_dvb *dvb = dev->dvb;
+       printk(KERN_INFO "tm6000: got start feed request %s\n",__FUNCTION__);
+
+       mutex_lock(&dvb->mutex);
+       if(dvb->streams == 0) {
+               dvb->streams = 1;
+//             mutex_init(&tm6000_dev->streaming_mutex);
+               tm6000_start_stream(dev);
+       }
+       else {
+               ++(dvb->streams);
+       }
+       mutex_unlock(&dvb->mutex);
+
+       return 0;
+}
+
+int tm6000_stop_feed(struct dvb_demux_feed *feed) {
+       struct dvb_demux *demux = feed->demux;
+       struct tm6000_core *dev = demux->priv;
+       struct tm6000_dvb *dvb = dev->dvb;
+
+       printk(KERN_INFO "tm6000: got stop feed request %s\n",__FUNCTION__);
+
+       mutex_lock(&dvb->mutex);
+       --dvb->streams;
+
+       if(0 == dvb->streams) {
+               tm6000_stop_stream(dev);
+//             mutex_destroy(&tm6000_dev->streaming_mutex);
+       }
+       mutex_unlock(&dvb->mutex);
+//     mutex_destroy(&tm6000_dev->streaming_mutex);
+
+       return 0;
+}
+
+int tm6000_dvb_attach_frontend(struct tm6000_core *dev)
+{
+       struct tm6000_dvb *dvb = dev->dvb;
+
+       if(dev->caps.has_zl10353) {
+               struct zl10353_config config =
+                                   {.demod_address = dev->demod_addr >> 1,
+                                    .no_tuner = 1,
+//                                  .input_frequency = 0x19e9,
+//                                  .r56_agc_targets =  0x1c,
+                                   };
+
+               dvb->frontend = pseudo_zl10353_attach(dev, &config,
+                                                          &dev->i2c_adap);
+       }
+       else {
+               printk(KERN_ERR "tm6000: no frontend defined for the device!\n");
+               return -1;
+       }
+
+       if(dvb->frontend) {
+               return 0;
+       }
+       else {
+               return -1;
+       }
+}
+
+int tm6000_dvb_register(struct tm6000_core *dev)
+{
+       int ret = -1;
+       struct tm6000_dvb *dvb = dev->dvb;
+
+       mutex_init(&dvb->mutex);
+
+       dvb->streams = 0;
+
+       /* attach the frontend */
+       ret = tm6000_dvb_attach_frontend(dev);
+       if(ret < 0) {
+               printk(KERN_ERR "tm6000: couldn't attach the frontend!\n");
+//             goto err;
+       }
+
+       ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T",
+                                                         THIS_MODULE, &dev->udev->dev);
+       dvb->adapter.priv = dev;
+
+       if(dvb->frontend) {
+               ret = dvb_register_frontend(&dvb->adapter, dvb->frontend);
+               if(ret < 0) {
+                       printk("tm6000: couldn't register frontend\n");
+                       goto adapter_err;
+               }
+
+               // attach the tuner like this for now
+               tm6000_i2c_call_clients(dev, VIDIOC_INT_DVB_TUNER_ATTACH, dvb->frontend);
+
+               printk("tm6000: XC2028/3028 asked to be attached to frontend!\n");
+       }
+       else {
+               printk("tm6000: no frontend found\n");
+       }
+
+       dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING
+                                                           | DMX_MEMORY_BASED_FILTERING;
+       dvb->demux.priv = dev;
+       dvb->demux.filternum = 256;
+       dvb->demux.feednum = 256;
+       dvb->demux.start_feed = tm6000_start_feed;
+       dvb->demux.stop_feed = tm6000_stop_feed;
+       dvb->demux.write_to_decoder = NULL;
+       ret = dvb_dmx_init(&dvb->demux);
+       if(ret < 0) {
+               printk("tm6000: dvb_dmx_init failed (errno = %d)\n", ret);
+               goto frontend_err;
+       }
+
+       dvb->dmxdev.filternum = dev->dvb->demux.filternum;
+       dvb->dmxdev.demux = &dev->dvb->demux.dmx;
+       dvb->dmxdev.capabilities = 0;
+
+       ret =  dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
+       if(ret < 0) {
+               printk("tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret);
+               goto dvb_dmx_err;
+       }
+
+       return 0;
+
+dvb_dmx_err:
+       dvb_dmx_release(&dvb->demux);
+frontend_err:
+       if(dvb->frontend) {
+               dvb_unregister_frontend(dvb->frontend);
+       }
+adapter_err:
+       dvb_unregister_adapter(&dvb->adapter);
+err:
+       return ret;
+}
+
+void tm6000_dvb_unregister(struct tm6000_core *dev)
+{
+       struct tm6000_dvb *dvb = dev->dvb;
+
+       if(dvb->bulk_urb != NULL) {
+               struct urb *bulk_urb = dvb->bulk_urb;
+
+               kfree(bulk_urb->transfer_buffer);
+               bulk_urb->transfer_buffer = NULL;
+               usb_unlink_urb(bulk_urb);
+               usb_free_urb(bulk_urb);
+       }
+
+//     mutex_lock(&tm6000_driver.open_close_mutex);
+       if(dvb->frontend) {
+               dvb_unregister_frontend(dvb->frontend);
+       }
+
+       dvb_dmxdev_release(&dvb->dmxdev);
+       dvb_dmx_release(&dvb->demux);
+       dvb_unregister_adapter(&dvb->adapter);
+       mutex_destroy(&dvb->mutex);
+//     mutex_unlock(&tm6000_driver.open_close_mutex);
+
+}
index 27bee62..3556fa4 100644 (file)
@@ -3,6 +3,9 @@
 
    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
 #include <linux/i2c.h>
 #include <linux/mutex.h>
 
+#include <linux/dvb/frontend.h>
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dmxdev.h"
+
 #define TM6000_VERSION KERNEL_VERSION(0, 0, 1)
 
 /* Inputs */
@@ -97,12 +105,23 @@ struct tm6000_capabilities {
        unsigned int    has_eeprom:1;
 };
 
+struct tm6000_dvb {
+       struct dvb_adapter      adapter;
+       struct dvb_demux        demux;
+       struct dvb_frontend     *frontend;
+       struct dmxdev           dmxdev;
+       unsigned int            streams;
+       struct urb              *bulk_urb;
+       struct mutex            mutex;
+};
+
 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 */
+
+       v4l2_std_id                     norm;           /* Current norm */
 
        enum tm6000_core_state          state;
 
@@ -136,6 +155,9 @@ struct tm6000_core {
 
        enum tm6000_mode                mode;
 
+       /* DVB-T support */
+       struct tm6000_dvb               *dvb;
+
        /* locks */
        struct mutex                    lock;
 
@@ -181,9 +203,13 @@ 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_init_digital_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_dvb_register(struct tm6000_core *dev);
+void tm6000_dvb_unregister(struct tm6000_core *dev);
+
 int tm6000_v4l2_register(struct tm6000_core *dev);
 int tm6000_v4l2_unregister(struct tm6000_core *dev);
 int tm6000_v4l2_exit(void);