V4L/DVB (10743): dm1105: not demuxing from interrupt context.

The driver already has DMA buffer organized like ringbuffer,
so it is easy to switch to a work queue.
Length of ringbuffer can easily be increased, if someone need it.

Signed-off-by: Igor M. Liplianin <liplianin@netup.ru>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
index f48f73a..d9f55be 100644
--- a/drivers/media/dvb/dm1105/dm1105.c
+++ b/drivers/media/dvb/dm1105/dm1105.c
@@ -220,10 +220,14 @@
 	/* i2c */
 	struct i2c_adapter i2c_adap;
 
+	/* irq */
+	struct work_struct work;
+
 	/* dma */
 	dma_addr_t dma_addr;
 	unsigned char *ts_buf;
 	u32 wrp;
+	u32 nextwrp;
 	u32 buffer_size;
 	unsigned int	PacketErrorCount;
 	unsigned int dmarst;
@@ -415,6 +419,9 @@
 	u8 data;
 	u16 keycode;
 
+	if (ir_debug)
+		printk(KERN_INFO "%s: received byte 0x%04x\n", __func__, ircom);
+
 	data = (ircom >> 8) & 0x7f;
 
 	input_event(ir->input_dev, EV_MSC, MSC_RAW, (0x0000f8 << 16) | data);
@@ -431,14 +438,45 @@
 
 }
 
+/* work handler */
+static void dm1105_dmx_buffer(struct work_struct *work)
+{
+	struct dm1105dvb *dm1105dvb =
+				container_of(work, struct dm1105dvb, work);
+	unsigned int nbpackets;
+	u32 oldwrp = dm1105dvb->wrp;
+	u32 nextwrp = dm1105dvb->nextwrp;
+
+	if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) &&
+			(dm1105dvb->ts_buf[oldwrp + 188] == 0x47) &&
+			(dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) {
+		dm1105dvb->PacketErrorCount++;
+		/* bad packet found */
+		if ((dm1105dvb->PacketErrorCount >= 2) &&
+				(dm1105dvb->dmarst == 0)) {
+			outb(1, dm_io_mem(DM1105_RST));
+			dm1105dvb->wrp = 0;
+			dm1105dvb->PacketErrorCount = 0;
+			dm1105dvb->dmarst = 0;
+			return;
+		}
+	}
+
+	if (nextwrp < oldwrp) {
+		memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size,
+						dm1105dvb->ts_buf, nextwrp);
+		nbpackets = ((dm1105dvb->buffer_size - oldwrp) + nextwrp) / 188;
+	} else
+		nbpackets = (nextwrp - oldwrp) / 188;
+
+	dm1105dvb->wrp = nextwrp;
+	dvb_dmx_swfilter_packets(&dm1105dvb->demux,
+					&dm1105dvb->ts_buf[oldwrp], nbpackets);
+}
+
 static irqreturn_t dm1105dvb_irq(int irq, void *dev_id)
 {
 	struct dm1105dvb *dm1105dvb = dev_id;
-	unsigned int piece;
-	unsigned int nbpackets;
-	u32 command;
-	u32 nextwrp;
-	u32 oldwrp;
 
 	/* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */
 	unsigned int intsts = inb(dm_io_mem(DM1105_INTSTS));
@@ -447,48 +485,17 @@
 	switch (intsts) {
 	case INTSTS_TSIRQ:
 	case (INTSTS_TSIRQ | INTSTS_IR):
-		nextwrp = inl(dm_io_mem(DM1105_WRP)) -
-			inl(dm_io_mem(DM1105_STADR)) ;
-		oldwrp = dm1105dvb->wrp;
-		spin_lock(&dm1105dvb->lock);
-		if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) &&
-				(dm1105dvb->ts_buf[oldwrp + 188] == 0x47) &&
-				(dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) {
-			dm1105dvb->PacketErrorCount++;
-			/* bad packet found */
-			if ((dm1105dvb->PacketErrorCount >= 2) &&
-					(dm1105dvb->dmarst == 0)) {
-				outb(1, dm_io_mem(DM1105_RST));
-				dm1105dvb->wrp = 0;
-				dm1105dvb->PacketErrorCount = 0;
-				dm1105dvb->dmarst = 0;
-				spin_unlock(&dm1105dvb->lock);
-				return IRQ_HANDLED;
-			}
-		}
-		if (nextwrp < oldwrp) {
-			piece = dm1105dvb->buffer_size - oldwrp;
-			memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size, dm1105dvb->ts_buf, nextwrp);
-			nbpackets = (piece + nextwrp)/188;
-		} else	{
-			nbpackets = (nextwrp - oldwrp)/188;
-		}
-		dvb_dmx_swfilter_packets(&dm1105dvb->demux, &dm1105dvb->ts_buf[oldwrp], nbpackets);
-		dm1105dvb->wrp = nextwrp;
-		spin_unlock(&dm1105dvb->lock);
+		dm1105dvb->nextwrp = inl(dm_io_mem(DM1105_WRP)) -
+					inl(dm_io_mem(DM1105_STADR));
+		schedule_work(&dm1105dvb->work);
 		break;
 	case INTSTS_IR:
-		command = inl(dm_io_mem(DM1105_IRCODE));
-		if (ir_debug)
-			printk("dm1105: received byte 0x%04x\n", command);
-
-		dm1105dvb->ir.ir_command = command;
+		dm1105dvb->ir.ir_command = inl(dm_io_mem(DM1105_IRCODE));
 		tasklet_schedule(&dm1105dvb->ir.ir_tasklet);
 		break;
 	}
+
 	return IRQ_HANDLED;
-
-
 }
 
 /* register with input layer */
@@ -710,7 +717,7 @@
 
 	dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL);
 	if (!dm1105dvb)
-		goto out;
+		return -ENOMEM;
 
 	dm1105dvb->pdev = pdev;
 	dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES;
@@ -740,13 +747,9 @@
 	spin_lock_init(&dm1105dvb->lock);
 	pci_set_drvdata(pdev, dm1105dvb);
 
-	ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED, DRIVER_NAME, dm1105dvb);
-	if (ret < 0)
-		goto err_pci_iounmap;
-
 	ret = dm1105dvb_hw_init(dm1105dvb);
 	if (ret < 0)
-		goto err_free_irq;
+		goto err_pci_iounmap;
 
 	/* i2c */
 	i2c_set_adapdata(&dm1105dvb->i2c_adap, dm1105dvb);
@@ -813,8 +816,15 @@
 
 	dvb_net_init(dvb_adapter, &dm1105dvb->dvbnet, dmx);
 	dm1105_ir_init(dm1105dvb);
-out:
-	return ret;
+
+	INIT_WORK(&dm1105dvb->work, dm1105_dmx_buffer);
+
+	ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED,
+						DRIVER_NAME, dm1105dvb);
+	if (ret < 0)
+		goto err_free_irq;
+
+	return 0;
 
 err_disconnect_frontend:
 	dmx->disconnect_frontend(dmx);
@@ -843,7 +853,7 @@
 err_kfree:
 	pci_set_drvdata(pdev, NULL);
 	kfree(dm1105dvb);
-	goto out;
+	return ret;
 }
 
 static void __devexit dm1105_remove(struct pci_dev *pdev)