V4L/DVB (13098): cx23885: Add integrated IR subdevice interrupt and notification...
authorAndy Walls <awalls@radix.net>
Sun, 27 Sep 2009 22:51:50 +0000 (19:51 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sat, 5 Dec 2009 20:40:20 +0000 (18:40 -0200)
Add integrated IR subdevice interrupt and notification handling.  This is in
preparation of input keypress handling changes for the cx23885 module.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/cx23885/Makefile
drivers/media/video/cx23885/cx23885-cards.c
drivers/media/video/cx23885/cx23885-core.c
drivers/media/video/cx23885/cx23885-ir.c [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-ir.h [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-reg.h
drivers/media/video/cx23885/cx23885.h

index 3832a1cb768d44c8a4bcc8268cf2ad9a9c189c0e..330cff3f77ac2acde6e5bf782e113b20427723d8 100644 (file)
@@ -1,6 +1,6 @@
 cx23885-objs   := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
                    cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
-                   cx23885-ioctl.o cx23888-ir.o \
+                   cx23885-ioctl.o cx23885-ir.o cx23888-ir.o \
                    netup-init.o cimax2.o netup-eeprom.o
 
 obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
index 7c5d13ac538ee98b18317356535797fad9f0053c..c0e2409f3cbd28f2c21198d8a7dc1420f801875a 100644 (file)
@@ -821,6 +821,7 @@ int cx23885_ir_init(struct cx23885_dev *dev)
                if (ret)
                        break;
                dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+               dev->pci_irqmask |= PCI_MSK_IR;
                break;
        case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
                request_module("ir-kbd-i2c");
@@ -830,6 +831,28 @@ int cx23885_ir_init(struct cx23885_dev *dev)
        return ret;
 }
 
+void cx23885_ir_fini(struct cx23885_dev *dev)
+{
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+               dev->pci_irqmask &= ~PCI_MSK_IR;
+               cx_clear(PCI_INT_MSK, PCI_MSK_IR);
+               cx23888_ir_remove(dev);
+               dev->sd_ir = NULL;
+               break;
+       }
+}
+
+void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
+{
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+               if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR))
+                       cx_set(PCI_INT_MSK, PCI_MSK_IR);
+               break;
+       }
+}
+
 void cx23885_card_setup(struct cx23885_dev *dev)
 {
        struct cx23885_tsport *ts1 = &dev->ts1;
index 73be16b0d2a026821c2239a38a561a5c167d488e..c879211a704db6aa06a4bbbe82363a73bf5840b7 100644 (file)
@@ -33,6 +33,7 @@
 #include "cx23885.h"
 #include "cimax2.h"
 #include "cx23888-ir.h"
+#include "cx23885-ir.h"
 
 MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
 MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
@@ -1655,6 +1656,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
        u32 ts1_status, ts1_mask;
        u32 ts2_status, ts2_mask;
        int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
+       bool ir_handled = false;
 
        pci_status = cx_read(PCI_INT_STAT);
        pci_mask = cx_read(PCI_INT_MSK);
@@ -1680,18 +1682,12 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
        dprintk(7, "ts2_status: 0x%08x  ts2_mask: 0x%08x count: 0x%x\n",
                ts2_status, ts2_mask, ts2_count);
 
-       if ((pci_status & PCI_MSK_RISC_RD) ||
-           (pci_status & PCI_MSK_RISC_WR) ||
-           (pci_status & PCI_MSK_AL_RD) ||
-           (pci_status & PCI_MSK_AL_WR) ||
-           (pci_status & PCI_MSK_APB_DMA) ||
-           (pci_status & PCI_MSK_VID_C) ||
-           (pci_status & PCI_MSK_VID_B) ||
-           (pci_status & PCI_MSK_VID_A) ||
-           (pci_status & PCI_MSK_AUD_INT) ||
-           (pci_status & PCI_MSK_AUD_EXT) ||
-           (pci_status & PCI_MSK_GPIO0) ||
-           (pci_status & PCI_MSK_GPIO1)) {
+       if (pci_status & (PCI_MSK_RISC_RD | PCI_MSK_RISC_WR |
+                         PCI_MSK_AL_RD   | PCI_MSK_AL_WR   | PCI_MSK_APB_DMA |
+                         PCI_MSK_VID_C   | PCI_MSK_VID_B   | PCI_MSK_VID_A   |
+                         PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
+                         PCI_MSK_GPIO0   | PCI_MSK_GPIO1   |
+                         PCI_MSK_IR)) {
 
                if (pci_status & PCI_MSK_RISC_RD)
                        dprintk(7, " (PCI_MSK_RISC_RD   0x%08x)\n",
@@ -1740,6 +1736,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
                if (pci_status & PCI_MSK_GPIO1)
                        dprintk(7, " (PCI_MSK_GPIO1     0x%08x)\n",
                                PCI_MSK_GPIO1);
+
+               if (pci_status & PCI_MSK_IR)
+                       dprintk(7, " (PCI_MSK_IR        0x%08x)\n",
+                               PCI_MSK_IR);
        }
 
        if (cx23885_boards[dev->board].cimax > 0 &&
@@ -1770,12 +1770,48 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
        if (vida_status)
                handled += cx23885_video_irq(dev, vida_status);
 
+       if (pci_status & PCI_MSK_IR) {
+               v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine,
+                                pci_status, &ir_handled);
+               if (ir_handled)
+                       handled++;
+       }
+
        if (handled)
                cx_write(PCI_INT_STAT, pci_status);
 out:
        return IRQ_RETVAL(handled);
 }
 
+static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
+                                   unsigned int notification, void *arg)
+{
+       struct cx23885_dev *dev;
+
+       if (sd == NULL)
+               return;
+
+       dev = to_cx23885(sd->v4l2_dev);
+
+       switch (notification) {
+       case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */
+               if (sd == dev->sd_ir)
+                       cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
+               break;
+       case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */
+               if (sd == dev->sd_ir)
+                       cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
+               break;
+       }
+}
+
+static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
+{
+       INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler);
+       INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
+       dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
+}
+
 static inline int encoder_on_portb(struct cx23885_dev *dev)
 {
        return cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER;
@@ -1872,6 +1908,9 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
        if (err < 0)
                goto fail_free;
 
+       /* Prepare to handle notifications from subdevices */
+       cx23885_v4l2_dev_notify_init(dev);
+
        /* pci init */
        dev->pci = pci_dev;
        if (pci_enable_device(pci_dev)) {
@@ -1914,6 +1953,13 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
                break;
        }
 
+       /*
+        * The CX2388[58] IR controller can start firing interrupts when
+        * enabled, so these have to take place after the cx23885_irq() handler
+        * is hooked up by the call to request_irq() above.
+        */
+       cx23885_ir_pci_int_enable(dev);
+
        return 0;
 
 fail_irq:
@@ -1930,9 +1976,9 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
        struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
        struct cx23885_dev *dev = to_cx23885(v4l2_dev);
 
-       cx23885_shutdown(dev);
+       cx23885_ir_fini(dev);
 
-       cx23888_ir_remove(dev);
+       cx23885_shutdown(dev);
 
        pci_disable_device(pci_dev);
 
diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c
new file mode 100644 (file)
index 0000000..e84f90c
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Infrared device support routines - non-input, non-vl42_subdev routines
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@radix.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 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <media/v4l2-device.h>
+
+#include "cx23885.h"
+
+#define CX23885_IR_RX_FIFO_SERVICE_REQ         0
+#define CX23885_IR_RX_END_OF_RX_DETECTED       1
+#define CX23885_IR_RX_HW_FIFO_OVERRUN          2
+#define CX23885_IR_RX_SW_FIFO_OVERRUN          3
+
+#define CX23885_IR_TX_FIFO_SERVICE_REQ         0
+
+
+void cx23885_ir_rx_work_handler(struct work_struct *work)
+{
+       struct cx23885_dev *dev =
+                            container_of(work, struct cx23885_dev, ir_rx_work);
+       u32 events = 0;
+       unsigned long *notifications = &dev->ir_rx_notifications;
+
+       if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications))
+               events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+       if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications))
+               events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+       if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications))
+               events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+       if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications))
+               events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+
+       if (events == 0)
+               return;
+}
+
+void cx23885_ir_tx_work_handler(struct work_struct *work)
+{
+       struct cx23885_dev *dev =
+                            container_of(work, struct cx23885_dev, ir_tx_work);
+       u32 events = 0;
+       unsigned long *notifications = &dev->ir_tx_notifications;
+
+       if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications))
+               events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+
+       if (events == 0)
+               return;
+
+}
+
+/* Called in an IRQ context */
+void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
+{
+       struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
+       unsigned long *notifications = &dev->ir_rx_notifications;
+
+       if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ)
+               set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications);
+       if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED)
+               set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications);
+       if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN)
+               set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
+       if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
+               set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
+       schedule_work(&dev->ir_rx_work);
+}
+
+/* Called in an IRQ context */
+void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
+{
+       struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
+       unsigned long *notifications = &dev->ir_tx_notifications;
+
+       if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
+               set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
+       schedule_work(&dev->ir_tx_work);
+}
diff --git a/drivers/media/video/cx23885/cx23885-ir.h b/drivers/media/video/cx23885/cx23885-ir.h
new file mode 100644 (file)
index 0000000..9b8a6d5
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Infrared device support routines - non-input, non-vl42_subdev routines
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@radix.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 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX23885_IR_H_
+#define _CX23885_IR_H_
+void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events);
+void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events);
+
+void cx23885_ir_rx_work_handler(struct work_struct *work);
+void cx23885_ir_tx_work_handler(struct work_struct *work);
+#endif
index eafbe5226bae4e2bce1694228bbacaa1a5419796..c0bc9a068954b91deb4e2c1f0c467693555b9ec5 100644 (file)
@@ -212,8 +212,9 @@ Channel manager Data Structure entry = 20 DWORD
 
 #define DEV_CNTRL2     0x00040000
 
-#define PCI_MSK_GPIO1   (1 << 24)
-#define PCI_MSK_GPIO0   (1 << 23)
+#define PCI_MSK_IR        (1 << 28)
+#define PCI_MSK_GPIO1     (1 << 24)
+#define PCI_MSK_GPIO0     (1 << 23)
 #define PCI_MSK_APB_DMA   (1 << 12)
 #define PCI_MSK_AL_WR     (1 << 11)
 #define PCI_MSK_AL_RD     (1 << 10)
index f7ed146566d951bd01698a18bd374f5e872b44a6..ce82698db5f76b3870a64bf39cb96c4731e44cb3 100644 (file)
@@ -355,7 +355,13 @@ struct cx23885_dev {
        unsigned char              radio_addr;
        unsigned int               has_radio;
        struct v4l2_subdev         *sd_cx25840;
-       struct v4l2_subdev         *sd_ir;
+
+       /* Infrared */
+       struct v4l2_subdev         *sd_ir;
+       struct work_struct         ir_rx_work;
+       unsigned long              ir_rx_notifications;
+       struct work_struct         ir_tx_work;
+       unsigned long              ir_tx_notifications;
 
        /* V4l */
        u32                        freq;
@@ -479,6 +485,8 @@ extern int cx23885_tuner_callback(void *priv, int component,
        int command, int arg);
 extern void cx23885_card_list(struct cx23885_dev *dev);
 extern int  cx23885_ir_init(struct cx23885_dev *dev);
+extern void cx23885_ir_pci_int_enable(struct cx23885_dev *dev);
+extern void cx23885_ir_fini(struct cx23885_dev *dev);
 extern void cx23885_gpio_setup(struct cx23885_dev *dev);
 extern void cx23885_card_setup(struct cx23885_dev *dev);
 extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev);