summaryrefslogtreecommitdiff
path: root/linux/drivers/media/dvb/ttpci
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/dvb/ttpci')
-rw-r--r--linux/drivers/media/dvb/ttpci/Kconfig68
-rw-r--r--linux/drivers/media/dvb/ttpci/Makefile13
-rw-r--r--linux/drivers/media/dvb/ttpci/budget-av.c395
-rw-r--r--linux/drivers/media/dvb/ttpci/budget-ci.c434
-rw-r--r--linux/drivers/media/dvb/ttpci/budget-core.c339
-rw-r--r--linux/drivers/media/dvb/ttpci/budget-patch.c335
-rw-r--r--linux/drivers/media/dvb/ttpci/budget.c267
-rw-r--r--linux/drivers/media/dvb/ttpci/budget.h86
8 files changed, 1935 insertions, 2 deletions
diff --git a/linux/drivers/media/dvb/ttpci/Kconfig b/linux/drivers/media/dvb/ttpci/Kconfig
index 8396852b0..64a4f9788 100644
--- a/linux/drivers/media/dvb/ttpci/Kconfig
+++ b/linux/drivers/media/dvb/ttpci/Kconfig
@@ -22,3 +22,71 @@ config DVB_AV7110_OSD
its menus, so say Y if you want to use this software.
All other people say N.
+
+config DVB_BUDGET
+ tristate "Budget cards"
+ depends on DVB_CORE
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder.
+
+ Say Y if you own such a card and want to use it.
+
+ This driver is available as a module called
+ dvb-ttpci-budget.o ( = code which can be inserted in
+ and removed from the running kernel whenever you want).
+ If you want to compile it as a module, say M
+ here and read <file:Documentation/modules.txt>.
+
+config DVB_BUDGET_CI
+ tristate "Budget cards with onboard CI connector"
+ depends on VIDEO_DEV && DVB_CORE && DVB_BUDGET
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder, but with onboard Common Interface connector.
+
+ Say Y if you own such a card and want to use it.
+
+ This driver is available as a module called
+ dvb-ttpci-budget-av.o ( = code which can be inserted in
+ and removed from the running kernel whenever you want).
+ If you want to compile it as a module, say M
+ here and read <file:Documentation/modules.txt>.
+
+config DVB_BUDGET_AV
+ tristate "Budget cards with analog video inputs"
+ depends on VIDEO_DEV && DVB_CORE && DVB_BUDGET
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder, but with one or more analog video inputs.
+
+ Say Y if you own such a card and want to use it.
+
+ This driver is available as a module called
+ dvb-ttpci-budget-av.o ( = code which can be inserted in
+ and removed from the running kernel whenever you want).
+ here and read <file:Documentation/modules.txt>.
+
+config DVB_BUDGET_PATCH
+ tristate "AV7110 cards with Budget Patch"
+ depends on DVB_CORE && DVB_BUDGET
+ help
+ Support for Budget Patch (full TS) modification on
+ SAA7146+AV7110 based cards (DVB-S cards). This
+ driver doesn't use onboard MPEG2 decoder. The
+ card is driven in Budget-only mode. Card is
+ required to have loaded firmware to tune properly.
+ Firmware can be loaded by insertion and removal of
+ standard AV7110 driver prior to loading this
+ driver.
+
+ Say Y if you own such a card and want to use it.
+
+ This driver is available as a module called
+ dvb-ttpci-budget-patch.o ( = code which can be inserted in
+ and removed from the running kernel whenever you want).
+ If you want to compile it as a module, say M
+ here and read <file:Documentation/modules.txt>.
diff --git a/linux/drivers/media/dvb/ttpci/Makefile b/linux/drivers/media/dvb/ttpci/Makefile
index 5a9fd0c0c..c1e1f7b4c 100644
--- a/linux/drivers/media/dvb/ttpci/Makefile
+++ b/linux/drivers/media/dvb/ttpci/Makefile
@@ -1,9 +1,18 @@
#
-# Makefile for the kernel AV7110 DVB device driver
+# Makefile for the kernel SAA7146 FULL TS DVB device driver
+# and the AV7110 DVB device driver
#
+dvb-ttpci-budget-objs := budget.o
+dvb-ttpci-budget-av-objs := budget-av.o
+dvb-ttpci-budget-ci-objs := budget-ci.o
+dvb-ttpci-budget-patch-objs := budget-patch.o
dvb-ttpci-objs := av7110.o av7110_ipack.o av7110_ir.o
+obj-$(CONFIG_DVB_BUDGET) += budget-core.o dvb-ttpci-budget.o
+obj-$(CONFIG_DVB_BUDGET_CI) += budget-core.o dvb-ttpci-budget-ci.o
+obj-$(CONFIG_DVB_BUDGET_AV) += budget-core.o dvb-ttpci-budget-av.o
+obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-core.o dvb-ttpci-budget-patch.o
obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o
-EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -I$(src)/../../common/ -I$(src)/../../common/saa7146
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/
diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c
new file mode 100644
index 000000000..66173a760
--- /dev/null
+++ b/linux/drivers/media/dvb/ttpci/budget-av.c
@@ -0,0 +1,395 @@
+/*
+ * budget-av.c: driver for the SAA7146 based Budget DVB cards
+ * with analog video in
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include "budget.h"
+#include "saa7146_vv.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME budget_av
+#endif
+
+
+struct budget_av {
+ struct budget budget;
+ struct video_device vd;
+ int cur_input;
+};
+
+/****************************************************************************
+ * INITIALIZATION
+ ****************************************************************************/
+
+static inline
+void ddelay(int i)
+{
+ current->state=TASK_INTERRUPTIBLE;
+ schedule_timeout((HZ*i)/100);
+}
+
+
+static
+u8 i2c_readreg (struct dvb_i2c_bus *i2c, u8 id, u8 reg)
+{
+ u8 mm1[] = {0x00};
+ u8 mm2[] = {0x00};
+ struct i2c_msg msgs[2];
+
+ msgs[0].flags = 0;
+ msgs[1].flags = I2C_M_RD;
+ msgs[0].addr = msgs[1].addr=id/2;
+ mm1[0] = reg;
+ msgs[0].len = 1; msgs[1].len = 1;
+ msgs[0].buf = mm1; msgs[1].buf = mm2;
+
+ i2c->xfer(i2c, msgs, 2);
+
+ return mm2[0];
+}
+
+
+static
+int i2c_writereg (struct dvb_i2c_bus *i2c, u8 id, u8 reg, u8 val)
+{
+ u8 msg[2]={ reg, val };
+ struct i2c_msg msgs;
+
+ msgs.flags=0;
+ msgs.addr=id/2;
+ msgs.len=2;
+ msgs.buf=msg;
+ return i2c->xfer (i2c, &msgs, 1);
+}
+
+
+static const
+u8 saa7113_tab[] = {
+ 0x01, 0x08,
+ 0x02, 0xc0,
+ 0x03, 0x33,
+ 0x04, 0x00,
+ 0x05, 0x00,
+ 0x06, 0xeb,
+ 0x07, 0xe0,
+ 0x08, 0x28,
+ 0x09, 0x00,
+ 0x0a, 0x80,
+ 0x0b, 0x47,
+ 0x0c, 0x40,
+ 0x0d, 0x00,
+ 0x0e, 0x01,
+ 0x0f, 0x44,
+
+ 0x10, 0x08,
+ 0x11, 0x0c,
+ 0x12, 0x7b,
+ 0x13, 0x00,
+ 0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
+
+ 0x57, 0xff,
+ 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07,
+ 0x5b, 0x83, 0x5e, 0x00,
+ 0xff
+};
+
+
+static
+int saa7113_init (struct budget_av *budget_av)
+{
+ struct budget *budget = &budget_av->budget;
+ const u8 *data = saa7113_tab;
+
+ if (i2c_writereg (budget->i2c_bus, 0x4a, 0x01, 0x08) != 1) {
+ DEB_D(("saa7113: not found on KNC card\n"));
+ return -ENODEV;
+ }
+
+ INFO(("saa7113: detected and initializing\n"));
+
+ while (*data != 0xff) {
+ i2c_writereg(budget->i2c_bus, 0x4a, *data, *(data+1));
+ data += 2;
+ }
+
+ DEB_D(("saa7113: status=%02x\n",
+ i2c_readreg(budget->i2c_bus, 0x4a, 0x1f)));
+
+ return 0;
+}
+
+
+static
+int saa7113_setinput (struct budget_av *budget_av, int input)
+{
+ struct budget *budget = &budget_av->budget;
+
+ if (input == 1) {
+ i2c_writereg(budget->i2c_bus, 0x4a, 0x02, 0xc7);
+ i2c_writereg(budget->i2c_bus, 0x4a, 0x09, 0x80);
+ } else if (input == 0) {
+ i2c_writereg(budget->i2c_bus, 0x4a, 0x02, 0xc0);
+ i2c_writereg(budget->i2c_bus, 0x4a, 0x09, 0x00);
+ } else
+ return -EINVAL;
+
+ budget_av->cur_input = input;
+ return 0;
+}
+
+
+static
+int budget_av_detach (struct saa7146_dev *dev)
+{
+ struct budget_av *budget_av = (struct budget_av*) dev->ext_priv;
+ int err;
+
+ DEB_EE(("dev: %p\n",dev));
+
+ saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO);
+
+ ddelay(20);
+
+ saa7146_unregister_device (&budget_av->vd, dev);
+
+ err = ttpci_budget_deinit (&budget_av->budget);
+
+ kfree (budget_av);
+
+ return err;
+}
+
+
+static
+int budget_av_attach (struct saa7146_dev* dev,
+ struct saa7146_pci_extension_data *info)
+{
+ struct budget_av *budget_av;
+ struct budget_info *bi = info->ext_priv;
+ int err;
+
+ DEB_EE(("dev: %p\n",dev));
+
+ if (bi->type != BUDGET_KNC1) {
+ return -ENODEV;
+ }
+
+ if (!(budget_av = kmalloc(sizeof(struct budget_av), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(budget_av, 0, sizeof(struct budget_av));
+
+ if ((err = ttpci_budget_init(&budget_av->budget, dev, info))) {
+ kfree(budget_av);
+ return err;
+ }
+
+ dev->ext_priv = budget_av;
+
+ /* knc1 initialization */
+ saa7146_write(dev, DD1_STREAM_B, 0x04000000);
+ saa7146_write(dev, DD1_INIT, 0x07000600);
+ saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26);
+
+ //test_knc_ci(av7110);
+
+ saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI);
+ ddelay(50);
+
+ if ((err = saa7113_init (budget_av))) {
+ budget_av_detach(dev);
+ return err;
+ }
+
+ saa7146_vv_init(dev);
+ if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1",
+ VFL_TYPE_GRABBER)))
+ {
+ ERR(("cannot register capture v4l2 device.\n"));
+ budget_av_detach(dev);
+ return err;
+ }
+
+ /* beware: this modifies dev->vv ... */
+ saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A,
+ SAA7146_HPS_SYNC_PORT_A);
+
+ saa7113_setinput (budget_av, 0);
+
+ /* what is this? since we don't support open()/close()
+ notifications, we simply put this into the release handler... */
+// saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO);
+ ddelay(20);
+
+ /* fixme: find some sane values here... */
+ saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
+
+ return 0;
+}
+
+
+
+#define KNC1_INPUTS 2
+static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
+ { 0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+ { 1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 },
+};
+
+
+static
+struct saa7146_extension_ioctls ioctls[] = {
+ { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
+ { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
+ { 0, 0 }
+};
+
+
+static
+int av_ioctl(struct saa7146_dev *dev, unsigned int cmd, void *arg)
+{
+ struct budget_av *budget_av = (struct budget_av*) dev->ext_priv;
+/*
+ struct saa7146_vv *vv = dev->vv_data;
+*/
+ switch(cmd) {
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *i = arg;
+
+ DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index));
+ if( i->index < 0 || i->index >= KNC1_INPUTS) {
+ return -EINVAL;
+ }
+ memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
+ return 0;
+ }
+ case VIDIOC_G_INPUT:
+ {
+ int *input = (int *)arg;
+
+ *input = budget_av->cur_input;
+
+ DEB_EE(("VIDIOC_G_INPUT %d.\n",*input));
+ return 0;
+ }
+ case VIDIOC_S_INPUT:
+ {
+ int input = *(int *)arg;
+ DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
+ return saa7113_setinput (budget_av, input);
+ }
+ default:
+/*
+ DEB2(printk("does not handle this ioctl.\n"));
+*/
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static
+struct saa7146_standard standard[] = {
+ { "PAL", V4L2_STD_PAL, SAA7146_PAL_VALUES },
+ { "NTSC", V4L2_STD_NTSC, SAA7146_NTSC_VALUES },
+};
+
+
+static
+struct saa7146_ext_vv vv_data = {
+ .inputs = 2,
+ .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113
+ .flags = 0,
+ .stds = &standard[0],
+ .num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
+ .ioctls = &ioctls[0],
+ .ioctl = av_ioctl,
+};
+
+
+
+static struct saa7146_extension budget_extension;
+
+
+MAKE_BUDGET_INFO(knc1, "KNC1 DVB-S", BUDGET_KNC1);
+
+static
+struct pci_device_id pci_tbl [] = {
+ MAKE_EXTENSION_PCI(knc1, 0x1131, 0x4f56),
+ {
+ .vendor = 0,
+ }
+};
+
+
+
+static
+struct saa7146_extension budget_extension = {
+ .name = "budget dvb /w video in\0",
+ .pci_tbl = pci_tbl,
+
+ .module = THIS_MODULE,
+ .attach = budget_av_attach,
+ .detach = budget_av_detach,
+
+ .ext_vv_data = &vv_data,
+
+ .irq_mask = MASK_10,
+ .irq_func = ttpci_budget_irq10_handler,
+};
+
+
+static
+int __init budget_av_init(void)
+{
+ DEB_EE((".\n"));
+
+ if (saa7146_register_extension(&budget_extension))
+ return -ENODEV;
+
+ return 0;
+}
+
+
+static
+void __exit budget_av_exit(void)
+{
+ DEB_EE((".\n"));
+ saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_av_init);
+module_exit(budget_av_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
+ "budget PCI DVB w/ analog input (e.g. the KNC cards)");
+
diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c
new file mode 100644
index 000000000..a4f5bad49
--- /dev/null
+++ b/linux/drivers/media/dvb/ttpci/budget-ci.c
@@ -0,0 +1,434 @@
+/*
+ * budget-ci.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * msp430 IR support contributed by Jack Thomasson <jkt@Helius.COM>
+ * partially based on the Siemens DVB driver by Ralph+Marcus Metzler
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include "budget.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME budget
+#endif
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#include "input_fake.h"
+#endif
+
+
+
+struct budget_ci {
+ struct budget budget;
+ struct input_dev input_dev;
+ struct tasklet_struct msp430_irq_tasklet;
+};
+
+
+
+#ifndef BORROWED_FROM_AV7110_H_BUT_REALLY_BELONGS_IN_SAA7146_DEFS_H
+
+#define DEBINOSWAP 0x000e0000
+#define GPIO_IRQHI 0x10
+#define GPIO_INPUT 0x00
+
+void gpio_set(struct saa7146_dev* saa, u8 pin, u8 data)
+{
+ u32 value = 0;
+
+ /* sanity check */
+ if(pin > 3)
+ return;
+
+ /* read old register contents */
+ value = saa7146_read(saa, GPIO_CTRL );
+
+ value &= ~(0xff << (8*pin));
+ value |= (data << (8*pin));
+
+ saa7146_write(saa, GPIO_CTRL, value);
+}
+
+
+
+static
+int wait_for_debi_done(struct saa7146_dev *saa)
+{
+ int start = jiffies;
+
+ /* wait for registers to be programmed */
+ while (1) {
+ if (saa7146_read(saa, MC2) & 2)
+ break;
+ if (jiffies - start > HZ / 20) {
+ printk ("DVB (%s): timed out while waiting"
+ " for registers getting programmed\n",
+ __FUNCTION__);
+ return -ETIMEDOUT;
+ }
+ }
+
+ /* wait for transfer to complete */
+ start = jiffies;
+ while (1) {
+ if (!(saa7146_read(saa, PSR) & SPCI_DEBI_S))
+ break;
+ saa7146_read(saa, MC2);
+ if (jiffies - start > HZ / 4) {
+ printk ("DVB (%s): timed out while waiting"
+ " for transfer completion\n",
+ __FUNCTION__);
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+
+static
+u32 debiread (struct saa7146_dev *saa, u32 config, int addr, int count)
+{
+ u32 result = 0;
+
+ if (count > 4 || count <= 0)
+ return 0;
+
+ if (wait_for_debi_done(saa) < 0)
+ return 0;
+
+ saa7146_write (saa, DEBI_COMMAND,
+ (count << 17) | 0x10000 | (addr & 0xffff));
+
+ saa7146_write(saa, DEBI_CONFIG, config);
+ saa7146_write(saa, MC2, (2 << 16) | 2);
+
+ wait_for_debi_done(saa);
+
+ result = saa7146_read(saa, DEBI_AD);
+ result &= (0xffffffffUL >> ((4 - count) * 8));
+
+ return result;
+}
+
+
+
+/* DEBI during interrupt */
+static inline
+u32 irdebi(struct saa7146_dev *saa, u32 config, int addr, u32 val, int count)
+{
+ u32 res;
+ res = debiread(saa, config, addr, count);
+ return res;
+}
+#endif
+
+
+
+
+/* from reading the following remotes:
+ Zenith Universal 7 / TV Mode 807 / VCR Mode 837
+ Hauppauge (from NOVA-CI-s box product)
+ i've taken a "middle of the road" approach and note the differences
+*/
+static
+ u16 key_map[64] = {
+ /* 0x0X */
+ KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8,
+ KEY_9,
+ KEY_ENTER,
+ 0,
+ KEY_POWER, /* RADIO on Hauppauge */
+ KEY_MUTE,
+ 0,
+ KEY_A, /* TV on Hauppauge */
+ /* 0x1X */
+ KEY_VOLUMEUP, KEY_VOLUMEDOWN,
+ 0, 0,
+ KEY_B,
+ 0, 0, 0, 0, 0, 0, 0,
+ KEY_UP, KEY_DOWN,
+ KEY_OPTION, /* RESERVED on Hauppauge */
+ 0,
+ /* 0x2X */
+ KEY_CHANNELUP, KEY_CHANNELDOWN,
+ KEY_PREVIOUS, /* Prev. Ch on Zenith, SOURCE on Hauppauge */
+ 0, 0, 0,
+ KEY_CYCLEWINDOWS, /* MINIMIZE on Hauppauge */
+ 0,
+ KEY_ENTER, /* VCR mode on Zenith */
+ KEY_PAUSE,
+ 0,
+ KEY_RIGHT, KEY_LEFT,
+ 0,
+ KEY_MENU, /* FULL SCREEN on Hauppauge */
+ 0,
+ /* 0x3X */
+ 0,
+ KEY_PREVIOUS, /* VCR mode on Zenith */
+ KEY_REWIND,
+ 0,
+ KEY_FASTFORWARD,
+ KEY_PLAY, KEY_STOP,
+ KEY_RECORD,
+ KEY_TUNER, /* TV/VCR on Zenith */
+ 0,
+ KEY_C,
+ 0,
+ KEY_EXIT,
+ 0,
+ KEY_TUNER, /* VCR mode on Zenith */
+ 0,
+};
+
+
+static
+void msp430_ir_debounce (unsigned long data)
+{
+ struct input_dev *dev = (struct input_dev *) data;
+
+ if (dev->rep[0] == 0 || dev->rep[0] == ~0) {
+ input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
+ return;
+ }
+
+ dev->rep[0] = 0;
+ dev->timer.expires = jiffies + HZ * 350 / 1000;
+ add_timer(&dev->timer);
+ input_event(dev, EV_KEY, key_map[dev->repeat_key], 2); /* REPEAT */
+}
+
+
+
+static
+void msp430_ir_interrupt (unsigned long data)
+{
+ struct budget_ci *budget_ci = (struct budget_ci*) data;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ struct input_dev *dev = &budget_ci->input_dev;
+ unsigned int code = irdebi(saa, DEBINOSWAP, 0x1234, 0, 2) >> 8;
+
+ if (code & 0x40) {
+ code &= 0x3f;
+
+ if (timer_pending(&dev->timer)) {
+ if (code == dev->repeat_key) {
+ ++dev->rep[0];
+ return;
+ }
+ del_timer(&dev->timer);
+ input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
+ }
+
+ if (!key_map[code]) {
+ printk ("DVB (%s): no key for %02x!\n",
+ __FUNCTION__, code);
+ return;
+ }
+
+ /* initialize debounce and repeat */
+ dev->repeat_key = code;
+ /* Zenith remote _always_ sends 2 sequences */
+ dev->rep[0] = ~0;
+ /* 350 milliseconds */
+ dev->timer.expires = jiffies + HZ * 350 / 1000;
+ /* MAKE */
+ input_event(dev, EV_KEY, key_map[code], !0);
+ add_timer(&dev->timer);
+ }
+}
+
+
+static
+int msp430_ir_init (struct budget_ci *budget_ci)
+{
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ int i;
+
+ memset(&budget_ci->input_dev, 0, sizeof(struct input_dev));
+
+ budget_ci->input_dev.name = saa->name;
+
+ set_bit(EV_KEY, budget_ci->input_dev.evbit);
+
+ for (i=0; i<sizeof(key_map)/sizeof(*key_map); i++)
+ if (key_map[i])
+ set_bit(key_map[i], budget_ci->input_dev.keybit);
+
+ input_register_device(&budget_ci->input_dev);
+
+ budget_ci->input_dev.timer.function = msp430_ir_debounce;
+
+ saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06);
+
+ gpio_set(saa, 3, GPIO_IRQHI);
+
+ return 0;
+}
+
+
+static
+void msp430_ir_deinit (struct budget_ci *budget_ci)
+{
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ struct input_dev *dev = &budget_ci->input_dev;
+
+ saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06);
+ gpio_set(saa, 3, GPIO_INPUT);
+ gpio_set(saa, 2, GPIO_INPUT);
+
+ if (del_timer(&dev->timer))
+ input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
+
+ input_unregister_device(dev);
+}
+
+
+static
+void budget_ci_irq (struct saa7146_dev *dev, u32 *isr)
+{
+ struct budget_ci *budget_ci = (struct budget_ci*) dev->ext_priv;
+
+ DEB_EE(("dev: %p, budget_ci: %p\n", dev, budget_ci));
+
+ if (*isr & MASK_06)
+ tasklet_schedule (&budget_ci->msp430_irq_tasklet);
+
+ if (*isr & MASK_10)
+ ttpci_budget_irq10_handler (dev, isr);
+}
+
+
+
+static
+int budget_ci_attach (struct saa7146_dev* dev,
+ struct saa7146_pci_extension_data *info)
+{
+ struct budget_ci *budget_ci;
+ int err;
+
+ if (!(budget_ci = kmalloc (sizeof(struct budget_ci), GFP_KERNEL)))
+ return -ENOMEM;
+
+ DEB_EE(("budget_ci: %p\n", budget_ci));
+
+ if ((err = ttpci_budget_init (&budget_ci->budget, dev, info))) {
+ kfree (budget_ci);
+ return err;
+ }
+
+ dev->ext_priv = budget_ci;
+
+ tasklet_init (&budget_ci->msp430_irq_tasklet, msp430_ir_interrupt,
+ (unsigned long) budget_ci);
+
+ msp430_ir_init (budget_ci);
+
+ return 0;
+}
+
+
+
+static
+int budget_ci_detach (struct saa7146_dev* dev)
+{
+ struct budget_ci *budget_ci = (struct budget_ci*) dev->ext_priv;
+ int err;
+
+ err = ttpci_budget_deinit (&budget_ci->budget);
+
+ tasklet_kill (&budget_ci->msp430_irq_tasklet);
+
+ msp430_ir_deinit (budget_ci);
+
+ kfree (budget_ci);
+
+ return err;
+}
+
+
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC);
+
+static
+struct pci_device_id pci_tbl[] = {
+ MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c),
+ MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f),
+ {
+ .vendor = 0,
+ }
+};
+
+
+
+static
+struct saa7146_extension budget_extension = {
+ .name = "budget_ci dvb\0",
+ .flags = 0,
+ .ext_vv_data = NULL,
+
+ .module = THIS_MODULE,
+ .pci_tbl = &pci_tbl[0],
+ .attach = budget_ci_attach,
+ .detach = budget_ci_detach,
+
+ .irq_mask = MASK_06 | MASK_10,
+ .irq_func = budget_ci_irq,
+};
+
+
+static
+int __init budget_ci_init(void)
+{
+ if (saa7146_register_extension(&budget_extension))
+ return -ENODEV;
+
+ return 0;
+}
+
+
+static
+void __exit budget_ci_exit(void)
+{
+ DEB_EE((".\n"));
+ saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_ci_init);
+module_exit(budget_ci_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hunold, Jack Thomasson, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
+ "budget PCI DVB cards w/ CI-module produced by "
+ "Siemens, Technotrend, Hauppauge");
+
diff --git a/linux/drivers/media/dvb/ttpci/budget-core.c b/linux/drivers/media/dvb/ttpci/budget-core.c
new file mode 100644
index 000000000..71cbcfffd
--- /dev/null
+++ b/linux/drivers/media/dvb/ttpci/budget-core.c
@@ -0,0 +1,339 @@
+#include "budget.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME budget
+#endif
+
+int budget_debug = 0;
+
+/****************************************************************************
+ * General helper functions
+ ****************************************************************************/
+
+static inline void ddelay(int i)
+{
+ current->state=TASK_INTERRUPTIBLE;
+ schedule_timeout((HZ*i)/100);
+}
+
+/****************************************************************************
+ * TT budget / WinTV Nova
+ ****************************************************************************/
+
+static
+int stop_ts_capture(struct budget *budget)
+{
+ DEB_EE(("budget: %p\n",budget));
+
+ if (--budget->feeding)
+ return budget->feeding;
+
+ saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off
+ IER_DISABLE(budget->dev, MASK_10);
+ return 0;
+}
+
+
+static
+int start_ts_capture (struct budget *budget)
+{
+ struct saa7146_dev *dev=budget->dev;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ if (budget->feeding)
+ return ++budget->feeding;
+
+ saa7146_write(dev, MC1, MASK_20); // DMA3 off
+
+ memset(budget->grabbing, 0x00, TS_HEIGHT*TS_WIDTH);
+
+ saa7146_write(dev, PCI_BT_V1, 0x001c0000 |
+ (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
+
+ budget->tsf=0xff;
+ budget->ttbp=0;
+ saa7146_write(dev, DD1_INIT, 0x02000600);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ saa7146_write(dev, BRS_CTRL, 0x60000000);
+ saa7146_write(dev, MC2, (MASK_08 | MASK_24));
+ mdelay(10);
+
+ saa7146_write(dev, BASE_ODD3, 0);
+ saa7146_write(dev, BASE_EVEN3, TS_WIDTH*TS_HEIGHT/2);
+ saa7146_write(dev, PROT_ADDR3, TS_WIDTH*TS_HEIGHT);
+ saa7146_write(dev, BASE_PAGE3, budget->pt.dma |ME1|0x90);
+ saa7146_write(dev, PITCH3, TS_WIDTH);
+
+ saa7146_write(dev, NUM_LINE_BYTE3, ((TS_HEIGHT/2)<<16)|TS_WIDTH);
+ saa7146_write(dev, MC2, (MASK_04 | MASK_20));
+ saa7146_write(dev, MC1, (MASK_04 | MASK_20)); // DMA3 on
+
+ IER_ENABLE(budget->dev, MASK_10); // VPE
+
+ return ++budget->feeding;
+}
+
+
+static
+void vpeirq (unsigned long data)
+{
+ struct budget *budget = (struct budget*) data;
+ u8 *mem = (u8 *)(budget->grabbing);
+ u32 olddma = budget->ttbp;
+ u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+
+ /* nearest lower position divisible by 188 */
+ newdma -= newdma % 188;
+
+ if (newdma >= TS_BUFLEN)
+ return;
+
+ budget->ttbp = newdma;
+
+ if(budget->feeding == 0 || newdma == olddma)
+ return;
+
+ if (newdma > olddma) { /* no wraparound, dump olddma..newdma */
+ if(mem[olddma] == 0x47)
+ dvb_dmx_swfilter_packets(&budget->demux,
+ mem+olddma, (newdma-olddma) / 188);
+ } else { /* wraparound, dump olddma..buflen and 0..newdma */
+ if(mem[olddma] == 0x47)
+ dvb_dmx_swfilter_packets(&budget->demux,
+ mem+olddma, (TS_BUFLEN-olddma) / 188);
+ if(mem[0] == 0x47)
+ dvb_dmx_swfilter_packets(&budget->demux,
+ mem, newdma / 188);
+ }
+}
+
+
+/****************************************************************************
+ * DVB API SECTION
+ ****************************************************************************/
+
+static
+int budget_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct budget *budget = (struct budget*) demux->priv;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ return start_ts_capture (budget);
+}
+
+static
+int budget_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct budget *budget = (struct budget *) demux->priv;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ return stop_ts_capture (budget);
+}
+
+
+static
+int budget_register(struct budget *budget)
+{
+ int ret;
+ dmx_frontend_t *dvbfront=&budget->hw_frontend;
+ struct dvb_demux *dvbdemux=&budget->demux;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ memcpy(budget->demux_id, "demux0_0", 9);
+ budget->demux_id[5] = budget->dvb_adapter->num + '0';
+ dvbdemux->priv = (void *) budget;
+
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = budget_start_feed;
+ dvbdemux->stop_feed = budget_stop_feed;
+ dvbdemux->write_to_decoder = NULL;
+
+ dvbdemux->dmx.vendor = "CIM";
+ dvbdemux->dmx.model = "sw";
+ dvbdemux->dmx.id = budget->demux_id;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING);
+
+ dvb_dmx_init(&budget->demux);
+
+ dvbfront->id = "hw_frontend";
+ dvbfront->vendor = "VLSI";
+ dvbfront->model = "DVB Frontend";
+ dvbfront->source = DMX_FRONTEND_0;
+
+ budget->dmxdev.filternum = 256;
+ budget->dmxdev.demux = &dvbdemux->dmx;
+ budget->dmxdev.capabilities = 0;
+
+ dvb_dmxdev_init(&budget->dmxdev, budget->dvb_adapter);
+
+ ret=dvbdemux->dmx.add_frontend (&dvbdemux->dmx,
+ &budget->hw_frontend);
+ if (ret < 0)
+ return ret;
+
+ budget->mem_frontend.id = "mem_frontend";
+ budget->mem_frontend.vendor = "memory";
+ budget->mem_frontend.model = "sw";
+ budget->mem_frontend.source = DMX_MEMORY_FE;
+ ret=dvbdemux->dmx.add_frontend (&dvbdemux->dmx,
+ &budget->mem_frontend);
+ if (ret<0)
+ return ret;
+
+ ret=dvbdemux->dmx.connect_frontend (&dvbdemux->dmx,
+ &budget->hw_frontend);
+ if (ret < 0)
+ return ret;
+
+ budget->dvb_net.card_num = budget->dvb_adapter->num;
+ dvb_net_init(budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx);
+
+ return 0;
+}
+
+
+static
+void budget_unregister(struct budget *budget)
+{
+ struct dvb_demux *dvbdemux=&budget->demux;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ dvb_net_release(&budget->dvb_net);
+
+ dvbdemux->dmx.close(&dvbdemux->dmx);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend);
+
+ dvb_dmxdev_release(&budget->dmxdev);
+ dvb_dmx_release(&budget->demux);
+}
+
+
+static
+int master_xfer (struct dvb_i2c_bus *i2c, const struct i2c_msg msgs[], int num)
+{
+ struct saa7146_dev *dev = i2c->data;
+ return saa7146_i2c_transfer(dev, msgs, num, 6);
+}
+
+
+int ttpci_budget_init (struct budget *budget,
+ struct saa7146_dev* dev,
+ struct saa7146_pci_extension_data *info)
+{
+ int length = TS_WIDTH*TS_HEIGHT;
+ int ret = 0;
+ struct budget_info *bi = info->ext_priv;
+
+ memset(budget, 0, sizeof(struct budget));
+
+ DEB_EE(("dev: %p, budget: %p\n", dev, budget));
+
+ budget->card = bi;
+ budget->dev = (struct saa7146_dev *) dev;
+
+ dvb_register_adapter(&budget->dvb_adapter, budget->card->name);
+
+ /* set dd1 stream a & b */
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, DD1_INIT, 0x02000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ /* the Siemens DVB needs this if you want to have the i2c chips
+ get recognized before the main driver is loaded */
+ saa7146_write(dev, GPIO_CTRL, 0x500000);
+
+ saa7146_i2c_adapter_prepare(dev, NULL, SAA7146_I2C_BUS_BIT_RATE_3200);
+
+ budget->i2c_bus = dvb_register_i2c_bus (master_xfer, dev,
+ budget->dvb_adapter, 0);
+
+ if (!budget->i2c_bus) {
+ dvb_unregister_adapter (budget->dvb_adapter);
+ return -ENOMEM;
+ }
+
+ if( NULL == (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci,length,&budget->pt))) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ saa7146_write(dev, PCI_BT_V1, 0x001c0000);
+ /* upload all */
+ saa7146_write(dev, GPIO_CTRL, 0x000000);
+
+ tasklet_init (&budget->vpe_tasklet, vpeirq, (unsigned long) budget);
+
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); /* frontend power on */
+
+ if (budget_register(budget) == 0)
+ return 0;
+
+err:
+ if (budget->grabbing)
+ vfree(budget->grabbing);
+
+ dvb_unregister_i2c_bus (master_xfer,budget->i2c_bus->adapter,
+ budget->i2c_bus->id);
+
+ dvb_unregister_adapter (budget->dvb_adapter);
+
+ return ret;
+}
+
+
+int ttpci_budget_deinit (struct budget *budget)
+{
+ struct saa7146_dev *dev = budget->dev;
+
+ DEB_EE(("budget: %p\n", budget));
+
+ budget_unregister (budget);
+
+ dvb_unregister_i2c_bus (master_xfer, budget->i2c_bus->adapter,
+ budget->i2c_bus->id);
+
+ dvb_unregister_adapter (budget->dvb_adapter);
+
+ tasklet_kill (&budget->vpe_tasklet);
+
+ saa7146_pgtable_free (dev->pci, &budget->pt);
+
+ vfree (budget->grabbing);
+ kfree (budget);
+
+ return 0;
+}
+
+void ttpci_budget_irq10_handler (struct saa7146_dev* dev, u32 *isr)
+{
+ struct budget *budget = (struct budget*)dev->ext_priv;
+
+ DEB_EE(("dev: %p, budget: %p\n",dev,budget));
+
+ if (*isr & MASK_10)
+ tasklet_schedule (&budget->vpe_tasklet);
+}
+
+
+EXPORT_SYMBOL_GPL(ttpci_budget_init);
+EXPORT_SYMBOL_GPL(ttpci_budget_deinit);
+EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler);
+EXPORT_SYMBOL_GPL(budget_debug);
+
+MODULE_PARM(budget_debug,"i");
+MODULE_LICENSE("GPL");
+
+
diff --git a/linux/drivers/media/dvb/ttpci/budget-patch.c b/linux/drivers/media/dvb/ttpci/budget-patch.c
new file mode 100644
index 000000000..7e1cb6d73
--- /dev/null
+++ b/linux/drivers/media/dvb/ttpci/budget-patch.c
@@ -0,0 +1,335 @@
+/*
+ * budget-patch.c: driver for Budget Patch,
+ * hardware modification of DVB-S cards enabling full TS
+ *
+ * Written by Emard <emard@softhome.net>
+ *
+ * Original idea by Roberto Deza <rdeza@unav.es>
+ *
+ * Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic
+ * and Metzlerbros
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include "budget.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME budget_patch
+#endif
+
+#define budget_patch budget
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(fs_1_3,"Siemens/Technotrend/Hauppauge PCI rev1.3+Budget_Patch", BUDGET_PATCH);
+
+static
+struct pci_device_id pci_tbl[] = {
+ MAKE_EXTENSION_PCI(fs_1_3,0x13c2, 0x0000),
+ {
+ .vendor = 0,
+ }
+};
+
+
+#define COMMAND (DPRAM_BASE + 0x0FC)
+#define DPRAM_BASE 0x4000
+#define DEBINOSWAP 0x000e0000
+
+
+typedef enum {
+ AudioDAC,
+ CabADAC,
+ ON22K,
+ OFF22K,
+ MainSwitch,
+ ADSwitch,
+ SendDiSEqC,
+ SetRegister
+} AUDCOM;
+
+
+typedef enum {
+ COMTYPE_NOCOM,
+ COMTYPE_PIDFILTER,
+ COMTYPE_MPEGDECODER,
+ COMTYPE_OSD,
+ COMTYPE_BMP,
+ COMTYPE_ENCODER,
+ COMTYPE_AUDIODAC,
+ COMTYPE_REQUEST,
+ COMTYPE_SYSTEM,
+ COMTYPE_REC_PLAY,
+ COMTYPE_COMMON_IF,
+ COMTYPE_PID_FILTER,
+ COMTYPE_PES,
+ COMTYPE_TS,
+ COMTYPE_VIDEO,
+ COMTYPE_AUDIO,
+ COMTYPE_CI_LL,
+} COMTYPE;
+
+
+static
+int wdebi(struct budget_patch *budget, u32 config, int addr, u32 val, int count)
+{
+ struct saa7146_dev *dev=budget->dev;
+
+ DEB_EE(("budget: %p\n", budget));
+
+ if (count <= 0 || count > 4)
+ return -1;
+
+ saa7146_write(dev, DEBI_CONFIG, config);
+
+ saa7146_write(dev, DEBI_AD, val );
+ saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff));
+ saa7146_write(dev, MC2, (2 << 16) | 2);
+ mdelay(5);
+
+ return 0;
+}
+
+
+static
+int SOutCommand(struct budget_patch *budget, u16* buf, int length)
+{
+ int i;
+
+ DEB_EE(("budget: %p\n", budget));
+
+ for (i = 2; i < length; i++)
+ wdebi(budget, DEBINOSWAP, COMMAND + 2*i, (u32) buf[i], 2);
+
+ if (length)
+ wdebi(budget, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2);
+ else
+ wdebi(budget, DEBINOSWAP, COMMAND + 2, 0, 2);
+
+ wdebi(budget, DEBINOSWAP, COMMAND, (u32) buf[0], 2);
+ return 0;
+}
+
+
+static
+void av7110_set22k(struct budget_patch *budget, int state)
+{
+ u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0};
+
+ DEB_EE(("budget: %p\n", budget));
+ SOutCommand(budget, buf, 2);
+}
+
+
+static int
+av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst)
+{
+ int i;
+ u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC),
+ 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ DEB_EE(("budget: %p\n", budget));
+
+ if (len>10)
+ len=10;
+
+ buf[1] = len+2;
+ buf[2] = len;
+
+ if (burst != -1)
+ buf[3]=burst ? 0x01 : 0x00;
+ else
+ buf[3]=0xffff;
+
+ for (i=0; i<len; i++)
+ buf[i+4]=msg[i];
+
+ SOutCommand(budget, buf, 18);
+ return 0;
+}
+
+
+int budget_patch_diseqc_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+{
+ struct budget_patch *budget = fe->before_after_data;
+
+ DEB_EE(("budget: %p\n", budget));
+
+ switch (cmd) {
+ case FE_SET_TONE:
+ switch ((fe_sec_tone_mode_t) arg) {
+ case SEC_TONE_ON:
+ av7110_set22k (budget, 1);
+ break;
+ case SEC_TONE_OFF:
+ av7110_set22k (budget, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case FE_DISEQC_SEND_MASTER_CMD:
+ {
+ struct dvb_diseqc_master_cmd *cmd = arg;
+
+ av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0);
+ break;
+ }
+
+ case FE_DISEQC_SEND_BURST:
+ av7110_send_diseqc_msg (budget, 0, NULL, (int) arg);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+
+static
+int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+{
+ struct budget_patch *budget;
+ int err;
+ int cnt;
+
+ if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL)))
+ return -ENOMEM;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ if ((err = ttpci_budget_init (budget, dev, info))) {
+ kfree (budget);
+ return err;
+ }
+
+/*
+** This code will setup the SAA7146_RPS1 to generate a square
+** wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of
+** TS_WIDTH packets) has been acquired on SAA7146_D1B video port;
+** then, this GPIO3 output which is connected to the D1B_VSYNC
+** input, will trigger the acquisition of the alternate field
+** and so on.
+** Currently, the TT_budget / WinTV_Nova cards have two ICs
+** (74HCT4040, LVC74) for the generation of this VSYNC signal,
+** which seems that can be done perfectly without this :-)).
+*/
+ cnt = 0; // Setup RPS1 "program" (p35)
+ // Wait reset Source Line Counter Threshold (p36)
+ dev->rps1[cnt++]=cpu_to_le32(CMD_PAUSE | RPS_INV | EVT_HS);
+ // Wait Source Line Counter Threshold (p36)
+ dev->rps1[cnt++]=cpu_to_le32(CMD_PAUSE | EVT_HS);
+ // Set GPIO3=1 (p42)
+ dev->rps1[cnt++]=cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+ dev->rps1[cnt++]=cpu_to_le32(GPIO3_MSK);
+ dev->rps1[cnt++]=cpu_to_le32(SAA7146_GPIO_OUTHI<<24);
+ // Wait reset Source Line Counter Threshold (p36)
+ dev->rps1[cnt++]=cpu_to_le32(CMD_PAUSE | RPS_INV | EVT_HS);
+ // Wait Source Line Counter Threshold
+ dev->rps1[cnt++]=cpu_to_le32(CMD_PAUSE | EVT_HS);
+ // Set GPIO3=0 (p42)
+ dev->rps1[cnt++]=cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2));
+ dev->rps1[cnt++]=cpu_to_le32(GPIO3_MSK);
+ dev->rps1[cnt++]=cpu_to_le32(SAA7146_GPIO_OUTLO<<24);
+ // Jump to begin of RPS program (p37)
+ dev->rps1[cnt++]=cpu_to_le32(CMD_JUMP);
+ dev->rps1[cnt++]=cpu_to_le32(virt_to_bus(&dev->rps1[0]));
+
+ // Fix VSYNC level
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+ // Set RPS1 Address register to point to RPS code (r108 p42)
+ saa7146_write(dev, RPS_ADDR1, virt_to_bus(&dev->rps1[0]));
+ // Set Source Line Counter Threshold, using BRS (rCC p43)
+ saa7146_write(dev, RPS_THRESH1, ((TS_HEIGHT/2) | MASK_12));
+ // Enable RPS1 (rFC p33)
+ saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+
+ dvb_add_frontend_ioctls (budget->dvb_adapter,
+ budget_patch_diseqc_ioctl, NULL, budget);
+
+ dev->ext_priv = budget;
+
+ return 0;
+}
+
+
+static
+int budget_patch_detach (struct saa7146_dev* dev)
+{
+ struct budget_patch *budget = (struct budget_patch*) dev->ext_priv;
+ int err;
+
+ dvb_remove_frontend_ioctls (budget->dvb_adapter,
+ budget_patch_diseqc_ioctl, NULL);
+
+ err = ttpci_budget_deinit (budget);
+
+ kfree (budget);
+
+ return err;
+}
+
+
+static
+int __init budget_patch_init(void)
+{
+ if (saa7146_register_extension(&budget_extension))
+ return -ENODEV;
+
+ return 0;
+}
+
+
+static
+void __exit budget_patch_exit(void)
+{
+ DEB_EE((".\n"));
+ saa7146_unregister_extension(&budget_extension);
+}
+
+
+static
+struct saa7146_extension budget_extension = {
+ .name = "budget_patch dvb\0",
+ .flags = 0,
+ .ext_vv_data = NULL,
+
+ .module = THIS_MODULE,
+ .pci_tbl = pci_tbl,
+ .attach = budget_patch_attach,
+ .detach = budget_patch_detach,
+
+ .irq_mask = MASK_10,
+ .irq_func = ttpci_budget_irq10_handler,
+};
+
+
+module_init(budget_patch_init);
+module_exit(budget_patch_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others");
+MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 "
+ "based so-called Budget Patch cards");
+
diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c
new file mode 100644
index 000000000..545101f44
--- /dev/null
+++ b/linux/drivers/media/dvb/ttpci/budget.c
@@ -0,0 +1,267 @@
+/*
+ * budget.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include "budget.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,51)
+ #define KBUILD_MODNAME budget
+#endif
+
+
+
+static inline void ddelay(int i)
+{
+ current->state=TASK_INTERRUPTIBLE;
+ schedule_timeout((HZ*i)/100);
+}
+
+
+static
+void Set22K (struct budget *budget, int state)
+{
+ struct saa7146_dev *dev=budget->dev;
+ DEB_EE(("budget: %p\n",budget));
+ saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
+}
+
+
+/* Diseqc functions only for TT Budget card */
+/* taken from the Skyvision DVB driver by
+ Ralph Metzler <rjkm@metzlerbros.de> */
+
+static
+void DiseqcSendBit (struct budget *budget, int data)
+{
+ struct saa7146_dev *dev=budget->dev;
+ DEB_EE(("budget: %p\n",budget));
+
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+ udelay(data ? 500 : 1000);
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+ udelay(data ? 1000 : 500);
+}
+
+
+static
+void DiseqcSendByte (struct budget *budget, int data)
+{
+ int i, par=1, d;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ for (i=7; i>=0; i--) {
+ d = (data>>i)&1;
+ par ^= d;
+ DiseqcSendBit(budget, d);
+ }
+
+ DiseqcSendBit(budget, par);
+}
+
+
+static
+int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, int burst)
+{
+ struct saa7146_dev *dev=budget->dev;
+ int i;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+ mdelay(16);
+
+ for (i=0; i<len; i++)
+ DiseqcSendByte(budget, msg[i]);
+
+ mdelay(16);
+
+ if (burst!=-1) {
+ if (burst)
+ DiseqcSendByte(budget, 0xff);
+ else {
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+ udelay(12500);
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+ }
+ ddelay(2);
+ }
+
+ return 0;
+}
+
+
+int budget_diseqc_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+{
+ struct budget *budget = fe->before_after_data;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ switch (cmd) {
+ case FE_SET_TONE:
+ switch ((fe_sec_tone_mode_t) arg) {
+ case SEC_TONE_ON:
+ Set22K (budget, 1);
+ break;
+ case SEC_TONE_OFF:
+ Set22K (budget, 0);
+ break;
+ default:
+ return -EINVAL;
+ };
+ break;
+
+ case FE_DISEQC_SEND_MASTER_CMD:
+ {
+ struct dvb_diseqc_master_cmd *cmd = arg;
+
+ SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
+ break;
+ }
+
+ case FE_DISEQC_SEND_BURST:
+ SendDiSEqCMsg (budget, 0, NULL, (int) arg);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ };
+
+ return 0;
+}
+
+
+static
+int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+{
+ struct budget *budget;
+ int err;
+
+ if (!(budget = kmalloc (sizeof(struct budget), GFP_KERNEL)))
+ return -ENOMEM;
+
+ DEB_EE(("budget: %p\n",budget));
+
+ if ((err = ttpci_budget_init (budget, dev, info))) {
+ kfree (budget);
+ return err;
+ }
+
+ dvb_add_frontend_ioctls (budget->dvb_adapter,
+ budget_diseqc_ioctl, NULL, budget);
+
+ dev->ext_priv = budget;
+
+ return 0;
+}
+
+
+static
+int budget_detach (struct saa7146_dev* dev)
+{
+ struct budget *budget = (struct budget*) dev->ext_priv;
+ int err;
+
+ dvb_remove_frontend_ioctls (budget->dvb_adapter,
+ budget_diseqc_ioctl, NULL);
+
+ err = ttpci_budget_deinit (budget);
+
+ kfree (budget);
+
+ return err;
+}
+
+
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC);
+/* Uncomment for Budget Patch */
+/*MAKE_BUDGET_INFO(fs_1_3,"Siemens/Technotrend/Hauppauge PCI rev1.3+Budget_Patch", BUDGET_PATCH);*/
+
+static
+struct pci_device_id pci_tbl[] = {
+ /* Uncomment for Budget Patch */
+ /*MAKE_EXTENSION_PCI(fs_1_3,0x13c2, 0x0000),*/
+ MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003),
+ MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004),
+ MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005),
+ MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
+ {
+ .vendor = 0,
+ }
+};
+
+
+
+static
+struct saa7146_extension budget_extension = {
+ .name = "budget dvb\0",
+ .flags = 0,
+ .ext_vv_data = NULL,
+
+ .module = THIS_MODULE,
+ .pci_tbl = pci_tbl,
+ .attach = budget_attach,
+ .detach = budget_detach,
+
+ .irq_mask = MASK_10,
+ .irq_func = ttpci_budget_irq10_handler,
+};
+
+
+static
+int __init budget_init(void)
+{
+ if (saa7146_register_extension(&budget_extension))
+ return -ENODEV;
+
+ return 0;
+}
+
+
+static
+void __exit budget_exit(void)
+{
+ DEB_EE((".\n"));
+ saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_init);
+module_exit(budget_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
+ "budget PCI DVB cards by Siemens, Technotrend, Hauppauge");
+
diff --git a/linux/drivers/media/dvb/ttpci/budget.h b/linux/drivers/media/dvb/ttpci/budget.h
new file mode 100644
index 000000000..09dbb2735
--- /dev/null
+++ b/linux/drivers/media/dvb/ttpci/budget.h
@@ -0,0 +1,86 @@
+#ifndef __BUDGET_DVB__
+#define __BUDGET_DVB__
+
+#include "dvb_i2c.h"
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+#include "demux.h"
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_filter.h"
+#include "dvb_net.h"
+#include "saa7146.h"
+
+
+extern int budget_debug;
+
+struct budget_info {
+ char *name;
+ int type;
+};
+
+/* place to store all the necessary device information */
+struct budget {
+
+ /* devices */
+ struct dvb_device dvb_dev;
+ dvb_net_t dvb_net;
+
+ struct saa7146_dev *dev;
+
+ struct dvb_i2c_bus *i2c_bus;
+ struct budget_info *card;
+
+ unsigned char *grabbing;
+ struct saa7146_pgtable pt;
+
+ struct tasklet_struct fidb_tasklet;
+ struct tasklet_struct vpe_tasklet;
+
+ dmxdev_t dmxdev;
+ struct dvb_demux demux;
+ char demux_id[16];
+
+ dmx_frontend_t hw_frontend;
+ dmx_frontend_t mem_frontend;
+
+ int fe_synced;
+ struct semaphore pid_mutex;
+
+ u8 tsf;
+ u32 ttbp;
+ int feeding;
+
+ struct dvb_adapter *dvb_adapter;
+ void *priv;
+};
+
+
+
+#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \
+static struct budget_info x_var ## _info = { \
+ .name=x_name, \
+ .type=x_type }; \
+static struct saa7146_pci_extension_data x_var = { \
+ .ext_priv = &x_var ## _info, \
+ .ext = &budget_extension };
+
+#define TS_WIDTH (4*188)
+#define TS_HEIGHT (1024/4)
+#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT)
+#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE)
+
+#define BUDGET_TT 0
+#define BUDGET_TT_HW_DISEQC 1
+#define BUDGET_KNC1 2
+#define BUDGET_PATCH 3
+
+
+extern int ttpci_budget_init (struct budget *budget,
+ struct saa7146_dev* dev,
+ struct saa7146_pci_extension_data *info);
+extern int ttpci_budget_deinit (struct budget *budget);
+extern void ttpci_budget_irq10_handler (struct saa7146_dev* dev, u32 *isr);
+
+#endif
+