Mailing List archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[linux-dvb] TT budget / NOVA IR support



the attached patch adds IR support for TT-PCline budget / Hauppauge
WinTV-Nova cards using MSP430 microcontrollers.  i developed against a
WinTV-Nova-CI-s but there may be other cards as well.  you'll find the
microcontroller just below the IR header (a sub-mini phono socket)
labelled MSP430F1101.

enjoy,

:{)}

diff -u -r1.7 Makefile
--- DVB/driver/av7110/Makefile	8 Nov 2002 15:28:08 -0000	1.7
+++ DVB/driver/av7110/Makefile	16 Jan 2003 22:26:33 -0000
@@ -23,6 +23,7 @@
 else
   obj-$(CONFIG_AV7110_DVB) += dvb-ttpci.o
 endif
+obj-m += msp430_ir.o
 
 # Extract lists of the multi-part drivers. 
 # The 'int-*' lists are the intermediate files used to build the multi's.

diff -u --unidirectional-new-file DVB-current/driver/av7110/msp430_ir.c DVB/driver/av7110/msp430_ir.c
--- DVB-current/driver/av7110/msp430_ir.c	1969-12-31 17:00:00.000000000 -0700
+++ DVB/driver/av7110/msp430_ir.c	2003-01-16 15:37:54.000000000 -0700
@@ -0,0 +1,287 @@
+/* Copyright (c) 2003 Helius, Inc.  All Rights Reserved. */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#include "input_fake.h"
+#endif
+
+#include "saa7146_defs.h"
+#include "saa7146_core.h"
+
+MODULE_AUTHOR("Helius, Inc.");
+MODULE_DESCRIPTION("input event module for those TechnoTrend TT-PCline budget/Hauppauge WinTV-Nova IR receivers using MSP430 microcontrollers");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
+/* 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,
+};
+
+
+#define __COMPILE_SAA7146_STUFF__
+#include "saa7146.c"
+
+#ifndef BORROWED_FROM_AV7110_H_BUT_REALLY_BELONGS_IN_SAA7146_DEFS_H
+/* DEBI transfer mode defs */
+
+#define DEBINOSWAP 0x000e0000
+#define DEBISWAB   0x001e0000
+#define DEBISWAP   0x002e0000
+
+typedef enum GPIO_MODE
+{
+	GPIO_INPUT = 0x00,
+	GPIO_IRQHI = 0x10,
+	GPIO_IRQLO = 0x20,
+	GPIO_IRQHL = 0x30,
+	GPIO_OUTLO = 0x40,
+	GPIO_OUTHI = 0x50
+} GPIO_MODE;
+#endif
+
+
+#ifndef BORROWED_FROM_AV7110_C_BUT_REALLY_BELONGS_IN_SAA7146_CORE_C
+/* This DEBI code is based on the Stradis driver 
+   by Nathan Laredo <laredo@gnu.org> */
+
+static
+int wait_for_debi_done(struct saa7146* saa)
+{
+	int start = jiffies;
+
+	/* wait for registers to be programmed */
+	while (1) {
+                if (saa7146_read(saa->mem, MC2) & 2)
+                        break;
+		if (jiffies-start > HZ/20) {
+			printk (KERN_WARNING __FUNCTION__": timed out while waiting"
+				" for registers getting programmed\n");
+			return -ETIMEDOUT;
+		}
+	}
+
+	/* wait for transfer to complete */
+	start = jiffies;
+	while (1) {
+		if (!(saa7146_read(saa->mem, PSR) & SPCI_DEBI_S))
+			break;
+		saa7146_read(saa->mem, MC2);
+		if (jiffies-start > HZ/4) {
+			printk (KERN_WARNING __FUNCTION__": timed out while waiting"
+				" for transfer completion\n");
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+
+static inline u32 debiread(struct saa7146* 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->mem, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
+	saa7146_write(saa->mem, DEBI_CONFIG, config);
+	saa7146_write(saa->mem, MC2, (2 << 16) | 2);
+	wait_for_debi_done(saa);
+	result = saa7146_read(saa->mem, DEBI_AD);
+        result &= (0xffffffffUL >> ((4-count)*8));
+	return result;
+}
+
+/* DEBI during interrupt */
+
+static inline u32
+irdebi(struct saa7146* saa, u32 config, int addr, u32 val, int count)
+{
+        u32 res;
+        res=debiread(saa, config, addr, count);
+        return res;
+}
+#endif
+
+
+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); /* BREAK */
+    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(struct saa7146* saa, u32 isr, void* data) {
+  unsigned int code = irdebi(saa, DEBINOSWAP, 0x1234, 0, 2) >> 8;
+  if (code & 0x40) {
+    struct input_dev* dev = (struct input_dev*)data;
+    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); /* BREAK */
+    }
+    if (!key_map[code]) {
+      printk(KERN_ERR __FUNCTION__": no key for %02x!\n", code);
+      return;
+    }
+    /* initialize debounce and repeat */
+    dev->repeat_key = code;
+    dev->rep[0] = ~0; /* Zenith remote _always_ sends 2 sequences */
+    dev->timer.expires = jiffies + HZ * 350 / 1000; /* 350 milliseconds */
+    add_timer(&dev->timer);
+    input_event(dev, EV_KEY, key_map[code], !0); /* MAKE */
+  }
+}
+
+
+static int msp430_ir_attach(struct saa7146* saa, void** priv_ptr) {
+  struct input_dev* dev;
+  int n;
+
+  if (saa->card_type != DVB_CARD_TT_BUDGET_CI) {
+    printk(KERN_ERR __FUNCTION__": rejected %s type=%d\n", saa->name, saa->card_type);
+    return -ENOENT;
+  }
+
+  if ((dev = (struct input_dev*)kmalloc(sizeof(struct input_dev), GFP_KERNEL)) == 0) {
+    printk(KERN_ERR __FUNCTION__": OOM!\n");
+    return -ENOMEM;
+  }
+  memset(dev, 0, sizeof(*dev));
+  dev->name = saa->name;
+  set_bit(EV_KEY, dev->evbit);
+  for (n = sizeof(key_map) / sizeof(*key_map); --n >= 0; )
+    if (key_map[n])
+      set_bit(key_map[n], dev->keybit);
+  input_register_device(dev);
+  /* input_dev timer and repeat stuff used only if EV_REP, initialized above, reuse */
+  dev->timer.function = msp430_ir_debounce;
+  *priv_ptr = dev;
+
+  /* reset MSP430 */
+  gpio_set(saa, 3, GPIO_OUTLO);
+  gpio_set(saa, 2, GPIO_OUTLO);
+  udelay(1000);				/* 1 millisecond */
+  saa7146_write(saa->mem, IER, saa7146_read(saa->mem, IER) | MASK_06);
+  gpio_set(saa, 3, GPIO_IRQHI);
+  gpio_set(saa, 2, GPIO_OUTHI);
+  printk(KERN_INFO "msp430_ir: attached %s\n", saa->name);
+  return 0;
+}
+
+
+static int msp430_ir_detach(struct saa7146* saa, void** priv_ptr) {
+  struct input_dev* dev = (struct input_dev*)*priv_ptr;
+  saa7146_write(saa->mem, IER, saa7146_read(saa->mem, 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); /* BREAK */
+  input_unregister_device(dev);
+  return 0;
+}
+
+
+static void msp430_ir_inc_use(struct saa7146* saa) {
+  MOD_INC_USE_COUNT;
+}
+
+
+static void msp430_ir_dec_use(struct saa7146* saa) {
+  MOD_DEC_USE_COUNT;
+}
+
+
+static struct saa7146_extension msp430_ir_extension = {
+  name: "msp430_ir",
+  handles_irqs: MASK_06,
+  irq_handler: msp430_ir_interrupt,
+  attach: msp430_ir_attach,
+  detach: msp430_ir_detach,
+  inc_use: msp430_ir_inc_use,
+  dec_use: msp430_ir_dec_use,
+};
+
+
+static int __init msp430_ir_init(void) {
+  int ret;
+  printk(KERN_INFO "msp430_ir: Copyright (c) 2003 Helius, Inc.  All Rights Reserved.\n");
+
+  if ((ret = saa7146_add_extension(&msp430_ir_extension)))
+    printk(KERN_ERR "msp430_ir: add_extension failed with %d\n", ret);
+  return ret;
+}
+
+
+static void __exit msp430_ir_exit(void) {
+  int ret;
+  if ((ret = saa7146_del_extension(&msp430_ir_extension)))
+    printk(KERN_ERR "msp430_ir: del_extension failed with %d\n", ret);
+}
+
+module_init(msp430_ir_init);
+module_exit(msp430_ir_exit);



Home | Main Index | Thread Index