};
 
 EXPORT_SYMBOL_GPL(ir_codes_norwood);
+
+/* From reading the following remotes:
+ * Zenith Universal 7 / TV Mode 807 / VCR Mode 837
+ * Hauppauge (from NOVA-CI-s box product)
+ * This is a "middle of the road" approach, differences are noted
+ */
+IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE] = {
+       [ 0x00 ] = KEY_0,
+       [ 0x01 ] = KEY_1,
+       [ 0x02 ] = KEY_2,
+       [ 0x03 ] = KEY_3,
+       [ 0x04 ] = KEY_4,
+       [ 0x05 ] = KEY_5,
+       [ 0x06 ] = KEY_6,
+       [ 0x07 ] = KEY_7,
+       [ 0x08 ] = KEY_8,
+       [ 0x09 ] = KEY_9,
+       [ 0x0a ] = KEY_ENTER,
+       [ 0x0b ] = KEY_RED,
+       [ 0x0c ] = KEY_POWER,             /* RADIO on Hauppauge */
+       [ 0x0d ] = KEY_MUTE,
+       [ 0x0f ] = KEY_A,                 /* TV on Hauppauge */
+       [ 0x10 ] = KEY_VOLUMEUP,
+       [ 0x11 ] = KEY_VOLUMEDOWN,
+       [ 0x14 ] = KEY_B,
+       [ 0x1c ] = KEY_UP,
+       [ 0x1d ] = KEY_DOWN,
+       [ 0x1e ] = KEY_OPTION,            /* RESERVED on Hauppauge */
+       [ 0x1f ] = KEY_BREAK,
+       [ 0x20 ] = KEY_CHANNELUP,
+       [ 0x21 ] = KEY_CHANNELDOWN,
+       [ 0x22 ] = KEY_PREVIOUS,          /* Prev. Ch on Zenith, SOURCE on Hauppauge */
+       [ 0x24 ] = KEY_RESTART,
+       [ 0x25 ] = KEY_OK,
+       [ 0x26 ] = KEY_CYCLEWINDOWS,      /* MINIMIZE on Hauppauge */
+       [ 0x28 ] = KEY_ENTER,             /* VCR mode on Zenith */
+       [ 0x29 ] = KEY_PAUSE,
+       [ 0x2b ] = KEY_RIGHT,
+       [ 0x2c ] = KEY_LEFT,
+       [ 0x2e ] = KEY_MENU,              /* FULL SCREEN on Hauppauge */
+       [ 0x30 ] = KEY_SLOW,
+       [ 0x31 ] = KEY_PREVIOUS,          /* VCR mode on Zenith */
+       [ 0x32 ] = KEY_REWIND,
+       [ 0x34 ] = KEY_FASTFORWARD,
+       [ 0x35 ] = KEY_PLAY,
+       [ 0x36 ] = KEY_STOP,
+       [ 0x37 ] = KEY_RECORD,
+       [ 0x38 ] = KEY_TUNER,             /* TV/VCR on Zenith */
+       [ 0x3a ] = KEY_C,
+       [ 0x3c ] = KEY_EXIT,
+       [ 0x3d ] = KEY_POWER2,
+       [ 0x3e ] = KEY_TUNER,
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old);
 
 #include <linux/interrupt.h>
 #include <linux/input.h>
 #include <linux/spinlock.h>
+#include <media/ir-common.h>
 
 #include "dvb_ca_en50221.h"
 #include "stv0299.h"
 #define SLOTSTATUS_READY       8
 #define SLOTSTATUS_OCCUPIED    (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
 
+/* Milliseconds during which key presses are regarded as key repeat and during
+ * which the debounce logic is active
+ */
+#define IR_REPEAT_TIMEOUT      350
+
+/* Some remotes sends multiple sequences per keypress (e.g. Zenith sends two),
+ * this setting allows the superflous sequences to be ignored
+ */
+static int debounce = 0;
+module_param(debounce, int, 0644);
+MODULE_PARM_DESC(debounce, "ignore repeated IR sequences (default: 0 = ignore no sequences)");
+
 struct budget_ci_ir {
        struct input_dev *dev;
        struct tasklet_struct msp430_irq_tasklet;
        char name[72]; /* 40 + 32 for (struct saa7146_dev).name */
        char phys[32];
+       struct ir_input_state state;
 };
 
 struct budget_ci {
        u8 tuner_pll_address; /* used for philips_tdm1316l configs */
 };
 
-/* 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,
-       KEY_RED,
-       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 */
-       KEY_BREAK,
-       /* 0x2X */
-       KEY_CHANNELUP, KEY_CHANNELDOWN,
-       KEY_PREVIOUS,           /* Prev. Ch on Zenith, SOURCE on Hauppauge */
-       0, KEY_RESTART, KEY_OK,
-       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 */
-       KEY_SLOW,
-       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,
-       KEY_POWER2,
-       KEY_TUNER,              /* VCR mode on Zenith */
-       0,
-};
+static void msp430_ir_keyup(unsigned long data)
+{
+       struct budget_ci_ir *ir = (struct budget_ci_ir *) data;
+       ir_input_nokey(ir->dev, &ir->state);
+}
+
+static void msp430_ir_interrupt(unsigned long data)
+{
+       struct budget_ci *budget_ci = (struct budget_ci *) data;
+       struct input_dev *dev = budget_ci->ir.dev;
+       static int bounces = 0;
+       u32 ir_key;
+       u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
+
+       if (command & 0x40) {
+               ir_key = command & 0x3f;
+
+               if (ir_key != dev->repeat_key && del_timer(&dev->timer))
+                       /* We were still waiting for a keyup event but this is a new key */
+                       ir_input_nokey(dev, &budget_ci->ir.state);
+
+               if (ir_key == dev->repeat_key && bounces > 0 && timer_pending(&dev->timer)) {
+                       /* Ignore repeated key sequences if requested */
+                       bounces--;
+                       return;
+               }
+
+               if (!timer_pending(&dev->timer))
+                       /* New keypress */
+                       bounces = debounce;
+
+               /* Prepare a keyup event sometime in the future */
+               mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT));
+
+               /* Generate a new or repeated keypress */
+               ir_input_keydown(dev, &budget_ci->ir.state, ir_key, command);
+       }
+}
 
 static void msp430_ir_debounce(unsigned long data)
 {
 {
        struct saa7146_dev *saa = budget_ci->budget.dev;
        struct input_dev *input_dev = budget_ci->ir.dev;
-       int i;
        int error;
 
        budget_ci->ir.dev = input_dev = input_allocate_device();
        input_dev->dev = &saa->pci->dev;
 # endif
 
-       set_bit(EV_KEY, input_dev->evbit);
-       for (i = 0; i < ARRAY_SIZE(key_map); i++)
-               if (key_map[i])
-                       set_bit(key_map[i], input_dev->keybit);
+       /* Select keymap */
+       switch (budget_ci->budget.dev->pci->subsystem_device) {
+       case 0x100c:
+       case 0x100f:
+       case 0x1010:
+       case 0x1011:
+       case 0x1012:
+       case 0x1017:
+               /* The hauppauge keymap is a superset of these remotes */
+               ir_input_init(input_dev, &budget_ci->ir.state,
+                             IR_TYPE_RC5, ir_codes_hauppauge_new);
+               break;
+       default:
+               /* unknown remote */
+               ir_input_init(input_dev, &budget_ci->ir.state,
+                             IR_TYPE_RC5, ir_codes_budget_ci_old);
+               break;
+       }
+
+       /* initialise the key-up timeout handler */
+       input_dev->timer.function = msp430_ir_keyup;
+       input_dev->timer.data = (unsigned long) &budget_ci->ir;
+       input_dev->rep[REP_DELAY] = 1;
+       input_dev->rep[REP_PERIOD] = 1;
 
        error = input_register_device(input_dev);
        if (error) {
                goto out2;
        }
 
-       input_dev->timer.function = msp430_ir_debounce;
-
        tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt,
                     (unsigned long) budget_ci);
 
        tasklet_kill(&budget_ci->ir.msp430_irq_tasklet);
 
        if (del_timer(&dev->timer)) {
-               input_event(dev, EV_KEY, key_map[dev->repeat_key], 0);
+               ir_input_nokey(dev, &budget_ci->ir.state);
                input_sync(dev);
        }