V4L/DVB (9168): Add support for MSI TV@nywhere Plus remote
authorBrian Rogers <brian_rogers@comcast.net>
Mon, 13 Oct 2008 11:37:06 +0000 (08:37 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 13 Oct 2008 11:57:34 +0000 (09:57 -0200)
The IR controller has a couple quirks. It won't respond until some other
device on the bus is probed. To work around that, probe 0x50 first.
Then, since it won't respond to a zero-byte read, probe with a one-byte
read.

Signed-off-by: Brian Rogers <brian_rogers@comcast.net>
[mchehab.redhat.com: Fix merge conflicts and remove an unused var]
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/common/ir-keymaps.c
drivers/media/video/ir-kbd-i2c.c
drivers/media/video/saa7134/saa7134-cards.c
drivers/media/video/saa7134/saa7134-i2c.c
drivers/media/video/saa7134/saa7134-input.c
include/media/ir-common.h

index b7ed88c..4952aeb 100644 (file)
@@ -517,7 +517,8 @@ EXPORT_SYMBOL_GPL(ir_codes_adstech_dvb_t_pci);
 
 /* ---------------------------------------------------------------------- */
 
-/* MSI TV@nywhere remote */
+/* MSI TV@nywhere MASTER remote */
+
 IR_KEYTAB_TYPE ir_codes_msi_tvanywhere[IR_KEYTAB_SIZE] = {
        /* Keys 0 to 9 */
        [ 0x00 ] = KEY_0,
@@ -551,6 +552,95 @@ EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere);
 
 /* ---------------------------------------------------------------------- */
 
+/*
+  Keycodes for remote on the MSI TV@nywhere Plus. The controller IC on the card
+  is marked "KS003". The controller is I2C at address 0x30, but does not seem
+  to respond to probes until a read is performed from a valid device.
+  I don't know why...
+
+  Note: This remote may be of similar or identical design to the
+  Pixelview remote (?).  The raw codes and duplicate button codes
+  appear to be the same.
+
+  Henry Wong <henry@stuffedcow.net>
+  Some changes to formatting and keycodes by Mark Schultz <n9xmj@yahoo.com>
+
+*/
+
+IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE] = {
+
+/*  ---- Remote Button Layout ----
+
+    POWER   SOURCE  SCAN    MUTE
+    TV/FM   1       2       3
+    |>      4       5       6
+    <|      7       8       9
+    ^^UP    0       +       RECALL
+    vvDN    RECORD  STOP    PLAY
+
+       MINIMIZE          ZOOM
+
+                 CH+
+      VOL-                   VOL+
+                 CH-
+
+       SNAPSHOT           MTS
+
+     <<      FUNC    >>     RESET
+*/
+
+       [0x01] = KEY_KP1,             /* 1 */
+       [0x0b] = KEY_KP2,             /* 2 */
+       [0x1b] = KEY_KP3,             /* 3 */
+       [0x05] = KEY_KP4,             /* 4 */
+       [0x09] = KEY_KP5,             /* 5 */
+       [0x15] = KEY_KP6,             /* 6 */
+       [0x06] = KEY_KP7,             /* 7 */
+       [0x0a] = KEY_KP8,             /* 8 */
+       [0x12] = KEY_KP9,             /* 9 */
+       [0x02] = KEY_KP0,             /* 0 */
+       [0x10] = KEY_KPPLUS,          /* + */
+       [0x13] = KEY_AGAIN,           /* Recall */
+
+       [0x1e] = KEY_POWER,           /* Power */
+       [0x07] = KEY_TUNER,           /* Source */
+       [0x1c] = KEY_SEARCH,          /* Scan */
+       [0x18] = KEY_MUTE,            /* Mute */
+
+       [0x03] = KEY_RADIO,           /* TV/FM */
+       /* The next four keys are duplicates that appear to send the
+          same IR code as Ch+, Ch-, >>, and << .  The raw code assigned
+          to them is the actual code + 0x20 - they will never be
+          detected as such unless some way is discovered to distinguish
+          these buttons from those that have the same code. */
+       [0x3f] = KEY_RIGHT,           /* |> and Ch+ */
+       [0x37] = KEY_LEFT,            /* <| and Ch- */
+       [0x2c] = KEY_UP,              /* ^^Up and >> */
+       [0x24] = KEY_DOWN,            /* vvDn and << */
+
+       [0x00] = KEY_RECORD,          /* Record */
+       [0x08] = KEY_STOP,            /* Stop */
+       [0x11] = KEY_PLAY,            /* Play */
+
+       [0x0f] = KEY_CLOSE,           /* Minimize */
+       [0x19] = KEY_ZOOM,            /* Zoom */
+       [0x1a] = KEY_SHUFFLE,         /* Snapshot */
+       [0x0d] = KEY_LANGUAGE,        /* MTS */
+
+       [0x14] = KEY_VOLUMEDOWN,      /* Vol- */
+       [0x16] = KEY_VOLUMEUP,        /* Vol+ */
+       [0x17] = KEY_CHANNELDOWN,     /* Ch- */
+       [0x1f] = KEY_CHANNELUP,       /* Ch+ */
+
+       [0x04] = KEY_REWIND,          /* << */
+       [0x0e] = KEY_MENU,            /* Function */
+       [0x0c] = KEY_FASTFORWARD,     /* >> */
+       [0x1d] = KEY_RESTART,         /* Reset */
+};
+EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_plus);
+
+/* ---------------------------------------------------------------------- */
+
 /* Cinergy 1400 DVB-T */
 IR_KEYTAB_TYPE ir_codes_cinergy_1400[IR_KEYTAB_SIZE] = {
        [ 0x01 ] = KEY_POWER,
index 703195a..efe8499 100644 (file)
  *      Markus Rechberger <mrechberger@gmail.com>
  * modified for DViCO Fusion HDTV 5 RT GOLD by
  *      Chaogui Zhang <czhang1974@gmail.com>
+ * modified for MSI TV@nywhere Plus by
+ *      Henry Wong <henry@stuffedcow.net>
+ *      Mark Schultz <n9xmj@yahoo.com>
+ *      Brian Rogers <brian_rogers@comcast.net>
  *
  *  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
@@ -242,9 +246,15 @@ static void ir_timer(unsigned long data)
 static void ir_work(struct work_struct *work)
 {
        struct IR_i2c *ir = container_of(work, struct IR_i2c, work);
+       int polling_interval = 100;
+
+       /* MSI TV@nywhere Plus requires more frequent polling
+          otherwise it will miss some keypresses */
+       if (ir->c.adapter->id == I2C_HW_SAA7134 && ir->c.addr == 0x30)
+               polling_interval = 50;
 
        ir_key_poll(ir);
-       mod_timer(&ir->timer, jiffies + msecs_to_jiffies(100));
+       mod_timer(&ir->timer, jiffies + msecs_to_jiffies(polling_interval));
 }
 
 /* ----------------------------------------------------------------------- */
@@ -483,9 +493,37 @@ static int ir_probe(struct i2c_adapter *adap)
                        (1 == rc) ? "yes" : "no");
                if (1 == rc) {
                        ir_attach(adap, probe[i], 0, 0);
-                       break;
+                       return 0;
                }
        }
+
+       /* Special case for MSI TV@nywhere Plus remote */
+       if (adap->id == I2C_HW_SAA7134) {
+               u8 temp;
+
+               /* MSI TV@nywhere Plus controller doesn't seem to
+                  respond to probes unless we read something from
+                  an existing device. Weird... */
+
+               msg.addr = 0x50;
+               rc = i2c_transfer(adap, &msg, 1);
+                       dprintk(1, "probe 0x%02x @ %s: %s\n",
+                       msg.addr, adap->name,
+                       (1 == rc) ? "yes" : "no");
+
+               /* Now do the probe. The controller does not respond
+                  to 0-byte reads, so we use a 1-byte read instead. */
+               msg.addr = 0x30;
+               msg.len = 1;
+               msg.buf = &temp;
+               rc = i2c_transfer(adap, &msg, 1);
+               dprintk(1, "probe 0x%02x @ %s: %s\n",
+                       msg.addr, adap->name,
+                       (1 == rc) ? "yes" : "no");
+               if (1 == rc)
+                       ir_attach(adap, msg.addr, 0, 0);
+       }
+
        return 0;
 }
 
index c9392c4..ddc5402 100644 (file)
@@ -5969,6 +5969,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
        case SAA7134_BOARD_PINNACLE_PCTV_110i:
        case SAA7134_BOARD_PINNACLE_PCTV_310i:
        case SAA7134_BOARD_UPMOST_PURPLE_TV:
+       case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
        case SAA7134_BOARD_HAUPPAUGE_HVR1110:
        case SAA7134_BOARD_BEHOLD_607_9FM:
        case SAA7134_BOARD_BEHOLD_M6:
index b02965c..20c1b33 100644 (file)
@@ -337,6 +337,7 @@ static int attach_inform(struct i2c_client *client)
                case 0x47:
                case 0x71:
                case 0x2d:
+               case 0x30:
                {
                        struct IR_i2c *ir = i2c_get_clientdata(client);
                        d1printk("%s i2c IR detected (%s).\n",
index 72a1c67..c53fd5f 100644 (file)
@@ -118,6 +118,53 @@ static int build_key(struct saa7134_dev *dev)
 
 /* --------------------- Chip specific I2C key builders ----------------- */
 
+static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key,
+                                      u32 *ir_raw)
+{
+       unsigned char b;
+       int gpio;
+
+       /* <dev> is needed to access GPIO. Used by the saa_readl macro. */
+       struct saa7134_dev *dev = ir->c.adapter->algo_data;
+       if (dev == NULL) {
+               dprintk("get_key_msi_tvanywhere_plus: "
+                       "gir->c.adapter->algo_data is NULL!\n");
+               return -EIO;
+       }
+
+       /* rising SAA7134_GPIO_GPRESCAN reads the status */
+
+       saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+       saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+       gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+       /* GPIO&0x40 is pulsed low when a button is pressed. Don't do
+          I2C receive if gpio&0x40 is not low. */
+
+       if (gpio & 0x40)
+               return 0;       /* No button press */
+
+       /* GPIO says there is a button press. Get it. */
+
+       if (1 != i2c_master_recv(&ir->c, &b, 1)) {
+               i2cdprintk("read error\n");
+               return -EIO;
+       }
+
+       /* No button press */
+
+       if (b == 0xff)
+               return 0;
+
+       /* Button pressed */
+
+       dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b);
+       *ir_key = b;
+       *ir_raw = b;
+       return 1;
+}
+
 static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
 {
        unsigned char b;
@@ -641,6 +688,11 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir)
                ir->get_key   = get_key_purpletv;
                ir->ir_codes  = ir_codes_purpletv;
                break;
+       case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
+               snprintf(ir->c.name, sizeof(ir->c.name), "MSI TV@nywhere Plus");
+               ir->get_key  = get_key_msi_tvanywhere_plus;
+               ir->ir_codes = ir_codes_msi_tvanywhere_plus;
+               break;
        case SAA7134_BOARD_HAUPPAUGE_HVR1110:
                snprintf(ir->c.name, sizeof(ir->c.name), "HVR 1110");
                ir->get_key   = get_key_hvr1110;
index 436360e..38f2d93 100644 (file)
@@ -156,6 +156,7 @@ extern IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE];
 extern IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE];
 extern IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE];
 extern IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE];
+extern IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE];
 #endif
 
 /*