[linux-dvb] PATCH: Nebula DigiTv PCI Remote Control support
Mark Weaver
mark-clist at npsl.co.uk
Sat Oct 1 05:12:49 CEST 2005
I've attached a patch that has this working well for me. This consists
of a new module, ir-kbd-nebula, and a patch against a few existing bttv
files to support it. I could not see how to sensibly glue this code
into ir-kbd-gpio as it is very different from the method most cards use.
The basic hooks in bttv are to add an `anyirq' flag, which forwards any
interrupt, not just GPINT, to the IR sub-driver if it has any any_irq
function. This is needed because somehow the nebula card generates an
interrupt without setting any bits in INT_STAT. I believe it is
triggering it off GPIO pin 5 somehow, but the nebula windows driver
doesn't even look in the stat bits -- it just tests GPIO pin 4. If
anyone can demystify me on that I'd be grateful, I just couldn't figure
out how this is possible from the bt878a datasheet.
I also set the has_remote flag & gpiomask (to a value obtained with the
DScaler regspy utility, I'm not sure if this is actually required for it
to work though).
The rest of the code (ir-kbd-nebula.c) is just some timers and bit
twiddling. I think the timing values might need to be adjusted a bit
for best 'feel', module parameters are:
key_timeout = 200ms. We don't get a key up event so I use a timer to
release the key.
repeat_period = 33ms. Standard input layer repeat period.
repeat_delay = 500ms. 2xStandard input layer repeat delay. Remote
buttons aren't as bouncy as keyboards, might be a bit long.
remote_gap = 885us. This is the inter-pulse distance, apparently there
is another remote that uses 600us. I don't have one so I don't know if
adjusting that value will work (depends on if it is RC5).
I'd be grateful for any test results &| code reviews. I'm not a kernel
programmer by trade so this would be helpful.
Thanks,
Mark
p.s. if required,
Signed-Off-By: Mark Weaver <mark at npsl.co.uk>
-------------- next part --------------
Index: Makefile
===================================================================
RCS file: /cvs/video4linux/video4linux/Makefile,v
retrieving revision 1.49
diff -u -r1.49 Makefile
--- Makefile 30 Sep 2005 04:21:04 -0000 1.49
+++ Makefile 1 Oct 2005 01:53:03 -0000
@@ -54,7 +54,7 @@
ifeq ($(VERSION).$(PATCHLEVEL),2.6)
ifeq ($(CONFIG_VIDEO_BTTV),m)
bttv-objs += bttv-gpio.o
- obj-$(CONFIG_VIDEO_IR) += ir-kbd-gpio.o ir-kbd-i2c.o
+ obj-$(CONFIG_VIDEO_IR) += ir-kbd-gpio.o ir-kbd-i2c.o ir-kbd-nebula.o
endif
ifeq ($(CONFIG_VIDEO_SAA7134),m)
obj-$(CONFIG_VIDEO_IR) += ir-kbd-i2c.o
@@ -136,7 +136,7 @@
inst_video := btcx-risc.ko bttv.ko tda9887.ko tuner.ko tvaudio.ko tveeprom.ko saa6588.ko
inst_video += tvmixer.ko v4l1-compat.ko v4l2-common.ko
inst_video += video-buf.ko video-buf-dvb.ko
-inst_video += ir-kbd-gpio.ko ir-kbd-i2c.ko msp3400.ko
+inst_video += ir-kbd-gpio.ko ir-kbd-i2c.ko msp3400.ko ir-kbd-nebula.o
inst_cx88 := cx8800.ko cx8802.ko cx88-alsa.ko
inst_cx88 += cx88-blackbird.ko cx88xx.ko cx88-dvb.ko
inst_saa7134 := saa6752hs.ko saa7134.ko saa7134-empress.ko saa7134-dvb.ko
Index: bttv-cards.c
===================================================================
RCS file: /cvs/video4linux/video4linux/bttv-cards.c,v
retrieving revision 1.85
diff -u -r1.85 bttv-cards.c
--- bttv-cards.c 30 Sep 2005 00:06:36 -0000 1.85
+++ bttv-cards.c 1 Oct 2005 01:53:07 -0000
@@ -2042,7 +2042,10 @@
.tuner_type = -1,
.tuner_addr = ADDR_UNSET,
.has_dvb = 1,
+ .has_remote = 1,
+ .gpiomask = 0x1b,
.no_gpioirq = 1,
+ .anyirq = 1,
},
[BTTV_BOARD_PV143] = {
/* Jorge Boncompte - DTI2 <jorge at dti2.net> */
@@ -3195,6 +3198,8 @@
btv->has_remote=1;
if (!bttv_tvcards[btv->c.type].no_gpioirq)
btv->gpioirq=1;
+ if (bttv_tvcards[btv->c.type].anyirq)
+ btv->anyirq = 1;
if (bttv_tvcards[btv->c.type].audio_hook)
btv->audio_hook=bttv_tvcards[btv->c.type].audio_hook;
Index: bttv-driver.c
===================================================================
RCS file: /cvs/video4linux/video4linux/bttv-driver.c,v
retrieving revision 1.60
diff -u -r1.60 bttv-driver.c
--- bttv-driver.c 29 Sep 2005 20:09:24 -0000 1.60
+++ bttv-driver.c 1 Oct 2005 01:53:10 -0000
@@ -3684,6 +3684,12 @@
int handled = 0;
btv=(struct bttv *)dev_id;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ if (btv->anyirq)
+ handled = bttv_any_irq(&btv->c);
+#endif
+
count=0;
while (1) {
/* get/clear interrupt status bits */
Index: bttv-gpio.c
===================================================================
RCS file: /cvs/video4linux/video4linux/bttv-gpio.c,v
retrieving revision 1.7
diff -u -r1.7 bttv-gpio.c
--- bttv-gpio.c 16 Feb 2005 12:14:10 -0000 1.7
+++ bttv-gpio.c 1 Oct 2005 01:53:10 -0000
@@ -114,6 +114,24 @@
}
}
+int bttv_any_irq(struct bttv_core *core)
+{
+ struct bttv_sub_driver *drv;
+ struct bttv_sub_device *dev;
+ struct list_head *item;
+ int handled = 0;
+
+ list_for_each(item,&core->subs) {
+ dev = list_entry(item,struct bttv_sub_device,list);
+ drv = to_bttv_sub_drv(dev->dev.driver);
+ if (drv && drv->any_irq) {
+ if (drv->any_irq(dev))
+ handled = 1;
+ }
+ }
+ return handled;
+}
+
/* ----------------------------------------------------------------------- */
/* external: sub-driver register/unregister */
Index: bttv.h
===================================================================
RCS file: /cvs/video4linux/video4linux/bttv.h,v
retrieving revision 1.26
diff -u -r1.26 bttv.h
--- bttv.h 29 Sep 2005 20:09:24 -0000 1.26
+++ bttv.h 1 Oct 2005 01:53:11 -0000
@@ -233,6 +233,7 @@
unsigned int has_dvb:1;
unsigned int has_remote:1;
unsigned int no_gpioirq:1;
+ unsigned int anyirq:1;
/* other settings */
unsigned int pll;
@@ -338,6 +339,7 @@
struct device_driver drv;
char wanted[BUS_ID_SIZE];
void (*gpio_irq)(struct bttv_sub_device *sub);
+ int (*any_irq)(struct bttv_sub_device *sub);
};
#define to_bttv_sub_drv(x) container_of((x), struct bttv_sub_driver, drv)
Index: bttvp.h
===================================================================
RCS file: /cvs/video4linux/video4linux/bttvp.h,v
retrieving revision 1.22
diff -u -r1.22 bttvp.h
--- bttvp.h 28 Sep 2005 19:07:59 -0000 1.22
+++ bttvp.h 1 Oct 2005 01:53:11 -0000
@@ -221,6 +221,7 @@
int bttv_sub_add_device(struct bttv_core *core, char *name);
int bttv_sub_del_devices(struct bttv_core *core);
void bttv_gpio_irq(struct bttv_core *core);
+int bttv_any_irq(struct bttv_core *core);
#endif
@@ -290,6 +291,7 @@
struct bttv_pll_info pll;
int triton1;
int gpioirq;
+ int anyirq;
int use_i2c_hw;
/* old gpio interface */
-------------- next part --------------
/*
* $Id:$
*
* Copyright (c) 2005 Mark Weaver
* Copyright (c) 2003 Gerd Knorr
* Copyright (c) 2003 Pavel Machek
*
* 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
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/pci.h>
#include <media/ir-common.h>
#include "bttv.h"
/* RC5 macros */
#define RC5_START(x) (((x)>>12)&3)
#define RC5_TOGGLE(x) (((x)>>11)&1)
#define RC5_ADDR(x) (((x)>>6)&31)
#define RC5_INSTR(x) ((x)&63)
/* ---------------------------------------------------------------------- */
static IR_KEYTAB_TYPE ir_codes_nebula[IR_KEYTAB_SIZE] = {
[ 0x00 ] = KEY_KP0,
[ 0x01 ] = KEY_KP1,
[ 0x02 ] = KEY_KP2,
[ 0x03 ] = KEY_KP3,
[ 0x04 ] = KEY_KP4,
[ 0x05 ] = KEY_KP5,
[ 0x06 ] = KEY_KP6,
[ 0x07 ] = KEY_KP7,
[ 0x08 ] = KEY_KP8,
[ 0x09 ] = KEY_KP9,
[ 0x0a ] = KEY_TV,
[ 0x0b ] = KEY_AUX,
[ 0x0c ] = KEY_DVD,
[ 0x0d ] = KEY_POWER,
[ 0x0e ] = KEY_MHP, /* labelled 'Picture' */
[ 0x0f ] = KEY_AUDIO,
[ 0x10 ] = KEY_INFO,
[ 0x11 ] = KEY_F13, /* 16:9 */
[ 0x12 ] = KEY_F14, /* 14:9 */
[ 0x13 ] = KEY_EPG,
[ 0x14 ] = KEY_EXIT,
[ 0x15 ] = KEY_MENU,
[ 0x16 ] = KEY_UP,
[ 0x17 ] = KEY_DOWN,
[ 0x18 ] = KEY_LEFT,
[ 0x19 ] = KEY_RIGHT,
[ 0x1a ] = KEY_ENTER,
[ 0x1b ] = KEY_CHANNELUP,
[ 0x1c ] = KEY_CHANNELDOWN,
[ 0x1d ] = KEY_VOLUMEUP,
[ 0x1e ] = KEY_VOLUMEDOWN,
[ 0x1f ] = KEY_RED,
[ 0x20 ] = KEY_GREEN,
[ 0x21 ] = KEY_YELLOW,
[ 0x22 ] = KEY_BLUE,
[ 0x23 ] = KEY_SUBTITLE,
[ 0x24 ] = KEY_F15, /* AD */
[ 0x25 ] = KEY_TEXT,
[ 0x26 ] = KEY_MUTE,
[ 0x27 ] = KEY_REWIND,
[ 0x28 ] = KEY_STOP,
[ 0x29 ] = KEY_PLAY,
[ 0x2a ] = KEY_FASTFORWARD,
[ 0x2b ] = KEY_F16, /* chapter */
[ 0x2c ] = KEY_PAUSE,
[ 0x2d ] = KEY_PLAY,
[ 0x2e ] = KEY_RECORD,
[ 0x2f ] = KEY_F17, /* picture in picture */
[ 0x30 ] = KEY_KPPLUS, /* zoom in */
[ 0x31 ] = KEY_KPMINUS, /* zoom out */
[ 0x32 ] = KEY_F18, /* capture */
[ 0x33 ] = KEY_F19, /* web */
[ 0x34 ] = KEY_EMAIL,
[ 0x35 ] = KEY_PHONE,
[ 0x36 ] = KEY_PC
};
struct IR {
struct bttv_sub_device *sub;
struct input_dev input;
struct ir_input_state ir;
char name[32];
char phys[32];
/* timer_end for code completion */
struct timer_list timer_end;
/* timer_end for key release */
struct timer_list timer_keyup;
/* last good rc5 code */
u32 last_rc5;
/* last raw bit seen */
u32 last_bit;
/* raw code under construction */
u32 code;
/* time of last seen code */
struct timeval base_time;
/* remote gap */
u32 remote_gap;
/* key up timer length */
u32 key_timeout;
/* building raw code */
int active;
};
static int debug;
module_param(debug, int, 0644); /* debug level (0,1,2) */
static int remote_gap = 885;
module_param(remote_gap, int, 0644);
static int repeat_delay = 500;
module_param(repeat_delay, int, 0644);
static int repeat_period = 33;
module_param(repeat_period, int, 0644);
static int key_timeout = 200;
module_param(key_timeout, int, 0644);
#define DEVNAME "ir-kbd-gpio"
#define dprintk(fmt, arg...) if (debug) \
printk(KERN_DEBUG DEVNAME ": " fmt , ## arg)
static int ir_irq(struct bttv_sub_device *sub);
static void ir_timer_end(unsigned long data);
static void ir_timer_keyup(unsigned long data);
static int ir_probe(struct device *dev);
static int ir_remove(struct device *dev);
static struct bttv_sub_driver driver = {
.drv = {
.name = DEVNAME,
.probe = ir_probe,
.remove = ir_remove,
},
.any_irq = ir_irq,
};
/* ---------------------------------------------------------------------- */
/* decode raw bit pattern to RC5 code */
static u32 rc5_decode(unsigned int code)
{
unsigned int org_code = code;
unsigned int pair;
unsigned int rc5 = 0;
int i;
code = (code << 1) | 1;
for (i = 0; i < 14; ++i)
{
pair = code & 0x3;
code >>= 2;
rc5 <<= 1;
switch (pair)
{
case 0:
case 2:
break;
case 1:
rc5 |= 1;
break;
case 3:
dprintk("bad code: %x\n", org_code);
return 0;
}
}
dprintk("code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
"instr=%x\n", rc5, org_code, RC5_START(rc5),
RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
return rc5;
}
static int ir_irq(struct bttv_sub_device *sub)
{
struct IR *ir = dev_get_drvdata(&sub->dev);
struct timeval tv;
u32 gpio;
u32 gap;
unsigned long current_jiffies, timeout;
/* read gpio port */
gpio = bttv_gpio_read(ir->sub->core);
/* remote IRQ? */
if (!(gpio & 0x20))
return 0;
/* get time of bit */
current_jiffies = jiffies;
do_gettimeofday(&tv);
/* avoid overflow with gap >1s */
if (tv.tv_sec - ir->base_time.tv_sec > 1) {
gap = 200000;
} else {
gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
tv.tv_usec - ir->base_time.tv_usec;
}
/* active code => add bit */
if (ir->active) {
/* only if in the code (otherwise spurious IRQ or timer
late) */
if (ir->last_bit < 28) {
ir->last_bit = (gap - ir->remote_gap / 2) /
ir->remote_gap;
ir->code |= 1 << ir->last_bit;
}
/* starting new code */
} else {
ir->active = 1;
ir->code = 0;
ir->base_time = tv;
ir->last_bit = 0;
timeout = current_jiffies + (500 + 30 * HZ) / 1000;
mod_timer(&ir->timer_end, timeout);
}
/* toggle GPIO pin 4 to reset the irq */
bttv_gpio_write(ir->sub->core, gpio & ~(1 << 4));
bttv_gpio_write(ir->sub->core, gpio | (1 << 4));
return 1;
}
static void ir_timer_end(unsigned long data)
{
struct IR *ir = (struct IR*)data;
struct timeval tv;
unsigned long current_jiffies, timeout;
u32 gap;
/* get time */
current_jiffies = jiffies;
do_gettimeofday(&tv);
/* avoid overflow with gap >1s */
if (tv.tv_sec - ir->base_time.tv_sec > 1) {
gap = 200000;
} else {
gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) +
tv.tv_usec - ir->base_time.tv_usec;
}
/* Allow some timmer jitter (RC5 is ~24ms anyway so this is ok) */
if (gap < 28000) {
dprintk("spurious timer_end\n");
return;
}
ir->active = 0;
if (ir->last_bit < 20) {
/* ignore spurious codes (caused by light/other remotes) */
dprintk("short code: %x\n", ir->code);
} else {
u32 rc5 = rc5_decode(ir->code);
/* two start bits? */
if (RC5_START(rc5) != 3) {
dprintk("rc5 start bits invalid: %u\n",
RC5_START(rc5));
/* right address? */
} else if (RC5_ADDR(rc5) == 0x0) {
u32 toggle = RC5_TOGGLE(rc5);
u32 instr = RC5_INSTR(rc5);
/* Good code, decide if repeat/repress */
if (toggle != RC5_TOGGLE(ir->last_rc5) ||
instr != RC5_INSTR(ir->last_rc5))
{
dprintk("instruction %x, toggle %x\n", instr,
toggle);
ir_input_nokey(&ir->input, &ir->ir);
ir_input_keydown(&ir->input, &ir->ir, instr,
instr);
}
/* Set/reset key-up timer */
timeout = current_jiffies + (500 + ir->key_timeout
* HZ) / 1000;
mod_timer(&ir->timer_keyup, timeout);
/* Save code for repeat test */
ir->last_rc5 = rc5;
}
}
}
static void ir_timer_keyup(unsigned long data)
{
struct IR *ir = (struct IR*)data;
dprintk("key released\n");
ir_input_nokey(&ir->input,&ir->ir);
}
/* ---------------------------------------------------------------------- */
static int ir_probe(struct device *dev)
{
struct bttv_sub_device *sub = to_bttv_sub_dev(dev);
struct IR *ir;
IR_KEYTAB_TYPE *ir_codes = NULL;
int ir_type = IR_TYPE_OTHER;
u32 gpio;
ir = kmalloc(sizeof(*ir),GFP_KERNEL);
if (NULL == ir)
return -ENOMEM;
memset(ir,0,sizeof(*ir));
ir->remote_gap = remote_gap;
ir->key_timeout = key_timeout;
/* detect & configure */
switch (sub->core->type) {
case BTTV_BOARD_NEBULA_DIGITV:
ir_codes = ir_codes_nebula;
break;
}
if (NULL == ir_codes) {
kfree(ir);
return -ENODEV;
}
/* enable remote irq */
bttv_gpio_inout(sub->core, (1<<4), 1<<4);
gpio = bttv_gpio_read(sub->core);
bttv_gpio_write(sub->core, gpio & ~(1 << 4));
bttv_gpio_write(sub->core, gpio | (1 << 4));
ir->sub = sub;
/* init input device */
snprintf(ir->name, sizeof(ir->name), "bttv IR (card=%d)",
sub->core->type);
snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
pci_name(sub->core->pci));
ir_input_init(&ir->input, &ir->ir, ir_type, ir_codes);
ir->input.name = ir->name;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
ir->input.phys = ir->phys;
ir->input.id.bustype = BUS_PCI;
ir->input.id.version = 1;
if (sub->core->pci->subsystem_vendor) {
ir->input.id.vendor = sub->core->pci->subsystem_vendor;
ir->input.id.product = sub->core->pci->subsystem_device;
} else {
ir->input.id.vendor = sub->core->pci->vendor;
ir->input.id.product = sub->core->pci->device;
}
ir->input.dev = &sub->core->pci->dev;
#endif
/* set timer_end for code completion */
init_timer(&ir->timer_end);
ir->timer_end.function = ir_timer_end;
ir->timer_end.data = (unsigned long)ir;
init_timer(&ir->timer_keyup);
ir->timer_keyup.function = ir_timer_keyup;
ir->timer_keyup.data = (unsigned long)ir;
/* all done */
dev_set_drvdata(dev,ir);
input_register_device(&ir->input);
/* the remote isn't as bouncy as a keyboard */
ir->input.rep[REP_DELAY] = repeat_delay;
ir->input.rep[REP_PERIOD] = repeat_period;
printk(DEVNAME ": %s detected at %s\n",ir->input.name,ir->input.phys);
return 0;
}
static int ir_remove(struct device *dev)
{
struct IR *ir = dev_get_drvdata(dev);
u32 gpio;
del_timer(&ir->timer_end);
flush_scheduled_work();
/* Disable IRQ */
gpio = bttv_gpio_read(ir->sub->core);
bttv_gpio_write(ir->sub->core, gpio & ~(1 << 4));
input_unregister_device(&ir->input);
kfree(ir);
return 0;
}
/* ---------------------------------------------------------------------- */
MODULE_AUTHOR("Gerd Knorr, Pavel Machek, Mark Weaver");
MODULE_DESCRIPTION("input driver for nebula bt8x8 gpio IR remote controls");
MODULE_LICENSE("GPL");
static int ir_init(void)
{
return bttv_sub_register(&driver, "remote");
}
static void ir_fini(void)
{
bttv_sub_unregister(&driver);
}
module_init(ir_init);
module_exit(ir_fini);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
More information about the linux-dvb
mailing list