V4L/DVB (8123): Add support for em2860 based PointNix Intra-Oral Camera
authorDevin Heitmueller <devin.heitmueller@gmail.com>
Sat, 28 Jun 2008 11:57:06 +0000 (08:57 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Sun, 20 Jul 2008 10:12:48 +0000 (07:12 -0300)
em28xx-cards.c
em28xx-input.c
em28xx-video.c
em28xx.h
 - Add support for the PointNix Intra-Oral Camera, which required addition of
   a construct for reading the "snapshot" button (provided on the em2860 and
   em2880 chips, but this is the first case where I have seen it actually used
   in a product). The button is wired to pin 56 on the em2880.

   http://www.pointnix.com/ENG/dental/product_02.asp

   Thanks to Roberto Mantovani <rmantovani@libero.it> for testing the changes

Signed-off-by: Devin Heitmueller <devin.heitmueller@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Documentation/video4linux/CARDLIST.em28xx
drivers/media/video/em28xx/em28xx-cards.c
drivers/media/video/em28xx/em28xx-input.c
drivers/media/video/em28xx/em28xx-video.c
drivers/media/video/em28xx/em28xx.h

index c7e23942c1dc20dd047b02ebd851515b521d64ee..10591467ef166aef609c2b527de16c9d17ca1ff1 100644 (file)
@@ -17,3 +17,4 @@
  16 -> Hauppauge WinTV HVR 950                  (em2880)        [2040:6513,2040:6517,2040:651b,2040:651f]
  17 -> Pinnacle PCTV HD Pro Stick               (em2880)        [2304:0227]
  18 -> Hauppauge WinTV HVR 900 (R2)             (em2880)        [2040:6502]
+ 19 -> PointNix Intra-Oral Camera               (em2860)
index 938c51e1c86dd6a804f9b1c594268d06afc69609..05f0d5a1505843edcdad70beec452456a52d17dd 100644 (file)
@@ -426,6 +426,19 @@ struct em28xx_board em28xx_boards[] = {
                        .amux     = EM28XX_AMUX_LINE_IN,
                } },
        },
+       [EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA] = {
+               .name         = "PointNix Intra-Oral Camera",
+               .has_snapshot_button = 1,
+               .vchannels    = 1,
+               .tda9887_conf = TDA9887_PRESENT,
+               .tuner_type   = TUNER_ABSENT,
+               .decoder      = EM28XX_SAA7113,
+               .input          = { {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = 0,
+               } },
+       },
 };
 const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
 
@@ -522,6 +535,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash [] = {
 static struct em28xx_hash_table em28xx_i2c_hash[] = {
        {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC},
        {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC},
+       {0x1ba50080, EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA, TUNER_ABSENT},
 };
 
 int em28xx_tuner_callback(void *ptr, int command, int arg)
@@ -554,6 +568,7 @@ static void em28xx_set_model(struct em28xx *dev)
        dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s;
        dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480;
        dev->has_dvb = em28xx_boards[dev->model].has_dvb;
+       dev->has_snapshot_button = em28xx_boards[dev->model].has_snapshot_button;
 }
 
 /* Since em28xx_pre_card_setup() requires a proper dev->model,
@@ -841,6 +856,9 @@ void em28xx_card_setup(struct em28xx *dev)
                        em28xx_set_model(dev);
        }
 
+       if (dev->has_snapshot_button)
+               em28xx_register_snapshot_button(dev);
+
        /* Allow override tuner type by a module parameter */
        if (tuner >= 0)
                dev->tuner_type = tuner;
index bb5807159b8d28638e46c01d7f0621861aedeed4..eab3d9511af3397c8aaa42be2cd116264d1ca892 100644 (file)
 
 #include "em28xx.h"
 
+#define EM28XX_SNAPSHOT_KEY KEY_CAMERA
+#define EM28XX_SBUTTON_QUERY_INTERVAL 500
+#define EM28XX_R0C_USBSUSP_SNAPSHOT 0x20
+
 static unsigned int ir_debug;
 module_param(ir_debug, int, 0644);
 MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
@@ -124,6 +128,89 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
        return 1;
 }
 
+static void em28xx_query_sbutton(struct work_struct *work)
+{
+       /* Poll the register and see if the button is depressed */
+       struct em28xx *dev =
+               container_of(work, struct em28xx, sbutton_query_work.work);
+       int ret;
+
+       ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP);
+
+       if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) {
+               u8 cleared;
+               /* Button is depressed, clear the register */
+               cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT;
+               em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1);
+
+               /* Not emulate the keypress */
+               input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
+                                1);
+               /* Now unpress the key */
+               input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
+                                0);
+       }
+
+       /* Schedule next poll */
+       schedule_delayed_work(&dev->sbutton_query_work,
+                             msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
+}
+
+void em28xx_register_snapshot_button(struct em28xx *dev)
+{
+       struct input_dev *input_dev;
+       int err;
+
+       em28xx_info("Registering snapshot button...\n");
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               em28xx_errdev("input_allocate_device failed\n");
+               return;
+       }
+
+       usb_make_path(dev->udev, dev->snapshot_button_path,
+                     sizeof(dev->snapshot_button_path));
+       strlcat(dev->snapshot_button_path, "/sbutton",
+               sizeof(dev->snapshot_button_path));
+       INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton);
+
+       input_dev->name = "em28xx snapshot button";
+       input_dev->phys = dev->snapshot_button_path;
+       input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+       set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit);
+       input_dev->keycodesize = 0;
+       input_dev->keycodemax = 0;
+       input_dev->id.bustype = BUS_USB;
+       input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+       input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+       input_dev->id.version = 1;
+       input_dev->dev.parent = &dev->udev->dev;
+
+       err = input_register_device(input_dev);
+       if (err) {
+               em28xx_errdev("input_register_device failed\n");
+               input_free_device(input_dev);
+               return;
+       }
+
+       dev->sbutton_input_dev = input_dev;
+       schedule_delayed_work(&dev->sbutton_query_work,
+                             msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
+       return;
+
+}
+
+void em28xx_deregister_snapshot_button(struct em28xx *dev)
+{
+       if (dev->sbutton_input_dev != NULL) {
+               em28xx_info("Deregistering snapshot button\n");
+               cancel_rearming_delayed_work(&dev->sbutton_query_work);
+               input_unregister_device(dev->sbutton_input_dev);
+               dev->sbutton_input_dev = NULL;
+       }
+       return;
+}
+
 /* ----------------------------------------------------------------------
  * Local variables:
  * c-basic-offset: 8
index 1c3e8b6291ea6c7b2446fb116dbde1523785a072..2d9f14d2a00b817119abeb1d5f29a2886014232d 100644 (file)
@@ -1590,6 +1590,8 @@ static void em28xx_release_resources(struct em28xx *dev)
                                dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN,
                                dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN);
        list_del(&dev->devlist);
+       if (dev->sbutton_input_dev)
+               em28xx_deregister_snapshot_button(dev);
        if (dev->radio_dev) {
                if (-1 != dev->radio_dev->minor)
                        video_unregister_device(dev->radio_dev);
index cff618b2d13cc18bb82e3cf19f3f27c55b80633c..89842c5d64a13da3e1204b41dace3fdc07eba8c0 100644 (file)
@@ -57,6 +57,7 @@
 #define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950   16
 #define EM2880_BOARD_PINNACLE_PCTV_HD_PRO      17
 #define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2        18
+#define EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA  19
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
@@ -249,6 +250,7 @@ struct em28xx_board {
        unsigned int has_12mhz_i2s:1;
        unsigned int max_range_640_480:1;
        unsigned int has_dvb:1;
+       unsigned int has_snapshot_button:1;
 
        enum em28xx_decoder decoder;
 
@@ -328,6 +330,7 @@ struct em28xx {
        unsigned int has_12mhz_i2s:1;
        unsigned int max_range_640_480:1;
        unsigned int has_dvb:1;
+       unsigned int has_snapshot_button:1;
 
        /* Some older em28xx chips needs a waiting time after writing */
        unsigned int wait_after_write;
@@ -418,6 +421,11 @@ struct em28xx {
        /* Caches GPO and GPIO registers */
        unsigned char   reg_gpo, reg_gpio;
 
+       /* Snapshot button */
+       char snapshot_button_path[30];  /* path of the input dev */
+       struct input_dev *sbutton_input_dev;
+       struct delayed_work sbutton_query_work;
+
        struct em28xx_dvb *dvb;
 };
 
@@ -483,6 +491,8 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
 int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw);
 int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
                                     u32 *ir_raw);
+void em28xx_register_snapshot_button(struct em28xx *dev);
+void em28xx_deregister_snapshot_button(struct em28xx *dev);
 
 /* printk macros */