Merge remote branch 'alsa/devel' into topic/misc
[pandora-kernel.git] / drivers / media / video / cx23885 / cx23885-core.c
index ff76f64..f6b62e7 100644 (file)
@@ -34,6 +34,7 @@
 #include "cimax2.h"
 #include "cx23888-ir.h"
 #include "cx23885-ir.h"
+#include "cx23885-av.h"
 #include "cx23885-input.h"
 
 MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
@@ -299,6 +300,83 @@ static struct sram_channel cx23887_sram_channels[] = {
        },
 };
 
+void cx23885_irq_add(struct cx23885_dev *dev, u32 mask)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       dev->pci_irqmask |= mask;
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       dev->pci_irqmask |= mask;
+       cx_set(PCI_INT_MSK, mask);
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask)
+{
+       u32 v;
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       v = mask & dev->pci_irqmask;
+       if (v)
+               cx_set(PCI_INT_MSK, v);
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_enable_all(struct cx23885_dev *dev)
+{
+       cx23885_irq_enable(dev, 0xffffffff);
+}
+
+void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       cx_clear(PCI_INT_MSK, mask);
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static inline void cx23885_irq_disable_all(struct cx23885_dev *dev)
+{
+       cx23885_irq_disable(dev, 0xffffffff);
+}
+
+void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       dev->pci_irqmask &= ~mask;
+       cx_clear(PCI_INT_MSK, mask);
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+}
+
+static u32 cx23885_irq_get_mask(struct cx23885_dev *dev)
+{
+       u32 v;
+       unsigned long flags;
+       spin_lock_irqsave(&dev->pci_irqmask_lock, flags);
+
+       v = cx_read(PCI_INT_MSK);
+
+       spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags);
+       return v;
+}
+
 static int cx23885_risc_decode(u32 risc)
 {
        static char *instr[16] = {
@@ -548,7 +626,7 @@ static void cx23885_shutdown(struct cx23885_dev *dev)
        cx_write(UART_CTL, 0);
 
        /* Disable Interrupts */
-       cx_write(PCI_INT_MSK, 0);
+       cx23885_irq_disable_all(dev);
        cx_write(VID_A_INT_MSK, 0);
        cx_write(VID_B_INT_MSK, 0);
        cx_write(VID_C_INT_MSK, 0);
@@ -774,6 +852,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
 {
        int i;
 
+       spin_lock_init(&dev->pci_irqmask_lock);
+
        mutex_init(&dev->lock);
        mutex_init(&dev->gpio_lock);
 
@@ -820,9 +900,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
 
        dev->pci_bus  = dev->pci->bus->number;
        dev->pci_slot = PCI_SLOT(dev->pci->devfn);
-       dev->pci_irqmask = 0x001f00;
+       cx23885_irq_add(dev, 0x001f00);
        if (cx23885_boards[dev->board].cimax > 0)
-               dev->pci_irqmask |= 0x01800000; /* for CiMaxes */
+               cx23885_irq_add(dev, 0x01800000); /* for CiMaxes */
 
        /* External Master 1 Bus */
        dev->i2c_bus[0].nr = 0;
@@ -1156,7 +1236,7 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
        dprintk(1, "%s() DEV_CNTRL2               0x%08X\n", __func__,
                cx_read(DEV_CNTRL2));
        dprintk(1, "%s() PCI_INT_MSK              0x%08X\n", __func__,
-               cx_read(PCI_INT_MSK));
+               cx23885_irq_get_mask(dev));
        dprintk(1, "%s() AUD_INT_INT_MSK          0x%08X\n", __func__,
                cx_read(AUDIO_INT_INT_MSK));
        dprintk(1, "%s() AUD_INT_DMA_CTL          0x%08X\n", __func__,
@@ -1292,7 +1372,8 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
                dprintk(1, "%s() enabling TS int's and DMA\n", __func__);
                cx_set(port->reg_ts_int_msk,  port->ts_int_msk_val);
                cx_set(port->reg_dma_ctl, port->dma_ctl_val);
-               cx_set(PCI_INT_MSK, dev->pci_irqmask | port->pci_irqmask);
+               cx23885_irq_add(dev, port->pci_irqmask);
+               cx23885_irq_enable_all(dev);
                break;
        default:
                BUG();
@@ -1650,10 +1731,10 @@ 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;
+       bool subdev_handled;
 
        pci_status = cx_read(PCI_INT_STAT);
-       pci_mask = cx_read(PCI_INT_MSK);
+       pci_mask = cx23885_irq_get_mask(dev);
        vida_status = cx_read(VID_A_INT_STAT);
        vida_mask = cx_read(VID_A_INT_MSK);
        ts1_status = cx_read(VID_B_INT_STAT);
@@ -1681,7 +1762,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
                          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)) {
+                         PCI_MSK_AV_CORE | PCI_MSK_IR)) {
 
                if (pci_status & PCI_MSK_RISC_RD)
                        dprintk(7, " (PCI_MSK_RISC_RD   0x%08x)\n",
@@ -1731,6 +1812,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
                        dprintk(7, " (PCI_MSK_GPIO1     0x%08x)\n",
                                PCI_MSK_GPIO1);
 
+               if (pci_status & PCI_MSK_AV_CORE)
+                       dprintk(7, " (PCI_MSK_AV_CORE   0x%08x)\n",
+                               PCI_MSK_AV_CORE);
+
                if (pci_status & PCI_MSK_IR)
                        dprintk(7, " (PCI_MSK_IR        0x%08x)\n",
                                PCI_MSK_IR);
@@ -1765,12 +1850,22 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
                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)
+               subdev_handled = false;
+               v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine,
+                                pci_status, &subdev_handled);
+               if (subdev_handled)
                        handled++;
        }
 
+       if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) {
+               cx23885_irq_disable(dev, PCI_MSK_AV_CORE);
+               if (!schedule_work(&dev->cx25840_work))
+                       printk(KERN_ERR "%s: failed to set up deferred work for"
+                              " AV Core/IR interrupt. Interrupt is disabled"
+                              " and won't be re-enabled\n", dev->name);
+               handled++;
+       }
+
        if (handled)
                cx_write(PCI_INT_STAT, pci_status);
 out:
@@ -1788,11 +1883,11 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
        dev = to_cx23885(sd->v4l2_dev);
 
        switch (notification) {
-       case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */
+       case V4L2_SUBDEV_IR_RX_NOTIFY: /* Possibly 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 */
+       case V4L2_SUBDEV_IR_TX_NOTIFY: /* Possibly called in an IRQ context */
                if (sd == dev->sd_ir)
                        cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
                break;
@@ -1801,6 +1896,7 @@ static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
 
 static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
 {
+       INIT_WORK(&dev->cx25840_work, cx23885_av_work_handler);
        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;
@@ -1967,7 +2063,7 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
 
        switch (dev->board) {
        case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
-               cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */
+               cx23885_irq_add_enable(dev, 0x01800000); /* for NetUP */
                break;
        }