[media] saa7164: Implement encoder irq handling in a deferred work queue
authorSteven Toth <stoth@kernellabs.com>
Sat, 31 Jul 2010 17:46:51 +0000 (14:46 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 21 Oct 2010 09:54:36 +0000 (07:54 -0200)
Signed-off-by: Steven Toth <stoth@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/saa7164/saa7164-core.c
drivers/media/video/saa7164/saa7164.h

index b8e56d8..591a3c1 100644 (file)
@@ -57,6 +57,10 @@ static unsigned int card[]  = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET };
 module_param_array(card,  int, NULL, 0444);
 MODULE_PARM_DESC(card, "card type");
 
+unsigned int print_histogram = 64;
+module_param(print_histogram, int, 0644);
+MODULE_PARM_DESC(debug, "print histogram values once");
+
 static unsigned int saa7164_devcount;
 
 static DEFINE_MUTEX(devlist);
@@ -64,49 +68,120 @@ LIST_HEAD(saa7164_devlist);
 
 #define INT_SIZE 16
 
-static void saa7164_work_cmdhandler(struct work_struct *w)
+static void saa7164_histogram_reset(struct saa7164_histogram *hg, char *name)
 {
-       struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd);
+       int i;
 
-       /* Wake up any complete commands */
-       saa7164_irq_dequeue(dev);
+       memset(hg, 0, sizeof(struct saa7164_histogram));
+       strcpy(hg->name, name);
+
+       /* First 30ms x 1ms */
+       for (i = 0; i < 30; i++) {
+               hg->counter1[0 + i].val = i;
+       }
+
+       /* 30 - 200ms x 10ms  */
+       for (i = 0; i < 18; i++) {
+               hg->counter1[30 + i].val = 30 + (i * 10);
+       }
+
+       /* 200 - 2000ms x 100ms  */
+       for (i = 0; i < 15; i++) {
+               hg->counter1[48 + i].val = 200 + (i * 100);
+       }
+
+       /* Catch all massive value (1hr) */
+       hg->counter1[63].val = 3600000;
 }
 
-static void saa7164_buffer_deliver(struct saa7164_buffer *buf)
+static void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val)
 {
-       struct saa7164_port *port = buf->port;
+       int i;
+       for (i = 0; i < 64; i++ ) {
+               if (val <= hg->counter1[i].val) {
+                       hg->counter1[i].count++;
+                       hg->counter1[i].update_time = jiffies;
+                       break;
+               }
+       }
+}
 
-       /* Feed the transport payload into the kernel demux */
-       dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu,
-               SAA7164_TS_NUMBER_OF_LINES);
+static void saa7164_histogram_print(struct saa7164_port *port,
+       struct saa7164_histogram *hg)
+{
+       struct saa7164_dev *dev = port->dev;
+       u32 entries = 0;
+       int i;
+
+       printk(KERN_ERR "Histogram named %s\n", hg->name);
+       for (i = 0; i < 64; i++ ) {
+               if (hg->counter1[i].count == 0)
+                       continue;
 
+               printk(KERN_ERR " %4d %12d %Ld\n",
+                       hg->counter1[i].val,
+                       hg->counter1[i].count,
+                       hg->counter1[i].update_time);
+
+               entries++;
+       }
+       printk(KERN_ERR "Total: %d\n", entries);
 }
 
-static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
+static void saa7164_work_enchandler(struct work_struct *w)
 {
+       struct saa7164_port *port =
+               container_of(w, struct saa7164_port, workenc);
        struct saa7164_dev *dev = port->dev;
        struct saa7164_buffer *buf;
        struct saa7164_user_buffer *ubuf;
        struct list_head *c, *n;
-       int wp, i = 0, rp;
+       int wp, rp, i = 0;
 
-       /* Find the current write point from the hardware */
-       wp = saa7164_readl(port->bufcounter);
-       if (wp > (port->hwcfg.buffercount - 1))
-               BUG();
+       port->last_svc_msecs_diff = port->last_svc_msecs;
+       port->last_svc_msecs = jiffies_to_msecs(jiffies);
+       port->last_svc_wp = saa7164_readl(port->bufcounter);
+       port->last_svc_rp = port->last_irq_rp;
+       wp = port->last_svc_wp;
+       rp = port->last_svc_rp;
 
-       /* Find the previous buffer to the current write point */
-       if (wp == 0)
-               rp = 7;
-       else
-               rp = wp - 1;
 
-       /* Lookup the WP in the buffer list */
-       /* TODO: turn this into a worker thread */
+       port->last_svc_msecs_diff = port->last_svc_msecs -
+               port->last_svc_msecs_diff;
+
+       saa7164_histogram_update(&port->svc_interval,
+               port->last_svc_msecs_diff);
+
+       port->last_irq_svc_msecs_diff = port->last_svc_msecs -
+               port->last_irq_msecs;
+
+       saa7164_histogram_update(&port->irq_svc_interval,
+               port->last_irq_svc_msecs_diff);
+
+       dprintk(DBGLVL_IRQ,
+               "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
+               __func__,
+               port->last_svc_msecs_diff,
+               port->last_irq_svc_msecs_diff,
+               port->last_svc_wp,
+               port->last_svc_rp
+               );
+
+       if ((rp < 0) || (rp > 7)) {
+               printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
+               return;
+       }
+
+       mutex_lock(&port->dmaqueue_lock);
+
        list_for_each_safe(c, n, &port->dmaqueue.list) {
+
                buf = list_entry(c, struct saa7164_buffer, list);
-               if (i++ > port->hwcfg.buffercount)
-                       BUG();
+               if (i++ > port->hwcfg.buffercount) {
+                       printk(KERN_ERR "%s() illegal i count %d\n",
+                               __func__, i);
+                       break;
+               }
 
                if (buf->idx == rp) {
                        /* Found the buffer, deal with it */
@@ -122,15 +197,14 @@ static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
                                        struct saa7164_user_buffer, list);
 
                                if (ubuf->actual_size == buf->actual_size)
-                                       memcpy(ubuf->data, buf->cpu, ubuf->actual_size);
+                                       memcpy(ubuf->data, buf->cpu,
+                                               ubuf->actual_size);
 
                                /* Requeue the buffer on the free list */
                                ubuf->pos = 0;
 
-
-//                             mutex_lock(&port->dmaqueue_lock);
-                               list_move_tail(&ubuf->list, &port->list_buf_used.list);
-//                             mutex_unlock(&port->dmaqueue_lock);
+                               list_move_tail(&ubuf->list,
+                                       &port->list_buf_used.list);
 
                                /* Flag any userland waiters */
                                wake_up_interruptible(&port->wait_read);
@@ -142,6 +216,81 @@ static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
                }
 
        }
+       mutex_unlock(&port->dmaqueue_lock);
+
+       if (print_histogram == port->nr) {
+               saa7164_histogram_print(port, &port->irq_interval);
+               saa7164_histogram_print(port, &port->svc_interval);
+               saa7164_histogram_print(port, &port->irq_svc_interval);
+               print_histogram = 64 + port->nr;
+       }
+}
+
+static void saa7164_work_cmdhandler(struct work_struct *w)
+{
+       struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd);
+
+       /* Wake up any complete commands */
+       saa7164_irq_dequeue(dev);
+}
+
+static void saa7164_buffer_deliver(struct saa7164_buffer *buf)
+{
+       struct saa7164_port *port = buf->port;
+
+       /* Feed the transport payload into the kernel demux */
+       dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu,
+               SAA7164_TS_NUMBER_OF_LINES);
+
+}
+
+static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
+{
+       struct saa7164_dev *dev = port->dev;
+       int wp, rp;
+
+       /* Find the current write point from the hardware */
+       wp = saa7164_readl(port->bufcounter);
+       if (wp > (port->hwcfg.buffercount - 1)) {
+               printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
+               return 0;
+       }
+
+       /* Find the previous buffer to the current write point */
+       if (wp == 0)
+               rp = 7;
+       else
+               rp = wp - 1;
+
+       if ((rp < 0) || (rp > 7)) {
+               printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
+               return 0;
+       }
+
+       /* Sore old time */
+       port->last_irq_msecs_diff = port->last_irq_msecs;
+
+       /* Collect new stats */
+       port->last_irq_msecs = jiffies_to_msecs(jiffies);
+       port->last_irq_wp = wp;
+       port->last_irq_rp = rp;
+
+       /* Calculate stats */
+       port->last_irq_msecs_diff = port->last_irq_msecs -
+               port->last_irq_msecs_diff;
+
+       saa7164_histogram_update(&port->irq_interval,
+               port->last_irq_msecs_diff);
+
+       dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed wp: %d rp: %d\n",
+               __func__,
+               port->last_irq_msecs_diff,
+               port->last_irq_wp,
+               port->last_irq_rp
+               );
+
+       schedule_work(&port->workenc);
+
        return 0;
 }
 
@@ -506,6 +655,15 @@ static int saa7164_port_init(struct saa7164_dev *dev, int portnr)
        INIT_LIST_HEAD(&port->list_buf_used.list);
        INIT_LIST_HEAD(&port->list_buf_free.list);
        init_waitqueue_head(&port->wait_read);
+
+       /* We need a deferred interrupt handler for cmd handling */
+       INIT_WORK(&port->workenc, saa7164_work_enchandler);
+
+       saa7164_histogram_reset(&port->irq_interval, "irq intervals");
+       saa7164_histogram_reset(&port->svc_interval, "deferred intervals");
+       saa7164_histogram_reset(&port->irq_svc_interval,
+               "irq to deferred intervals");
+
        return 0;
 }
 
@@ -784,6 +942,13 @@ static void __devexit saa7164_finidev(struct pci_dev *pci_dev)
 {
        struct saa7164_dev *dev = pci_get_drvdata(pci_dev);
 
+       saa7164_histogram_print(&dev->ports[ SAA7164_PORT_ENC1 ],
+               &dev->ports[ SAA7164_PORT_ENC1 ].irq_interval);
+       saa7164_histogram_print(&dev->ports[ SAA7164_PORT_ENC1 ],
+               &dev->ports[ SAA7164_PORT_ENC1 ].svc_interval);
+       saa7164_histogram_print(&dev->ports[ SAA7164_PORT_ENC1 ],
+               &dev->ports[ SAA7164_PORT_ENC1 ].irq_svc_interval);
+
        saa7164_shutdown(dev);
 
        if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB)
index ad39072..a262875 100644 (file)
@@ -176,6 +176,17 @@ struct saa7164_fh {
        atomic_t v4l_reading;
 };
 
+struct saa7164_histogram_bucket {
+       u32 val;
+       u32 count;
+       u64 update_time;
+};
+
+struct saa7164_histogram {
+       char name[32];
+       struct saa7164_histogram_bucket counter1[64];
+};
+
 struct saa7164_user_buffer {
        struct list_head list;
 
@@ -308,6 +319,16 @@ struct saa7164_port {
        struct mutex dmaqueue_lock;
        struct saa7164_buffer dmaqueue;
 
+       u64 last_irq_msecs, last_svc_msecs;
+       u64 last_irq_msecs_diff, last_svc_msecs_diff;
+       u32 last_irq_wp, last_svc_wp;
+       u32 last_irq_rp, last_svc_rp;
+       u64 last_irq_svc_msecs_diff;
+
+       struct saa7164_histogram irq_interval;
+       struct saa7164_histogram svc_interval;
+       struct saa7164_histogram irq_svc_interval;
+
        /* --- DVB Transport Specific --- */
        struct saa7164_dvb dvb;
 
@@ -338,6 +359,8 @@ struct saa7164_port {
        tmComResExtDevDescrHeader_t ifunit;
        tmComResTunerDescrHeader_t tunerunit;
 
+       struct work_struct workenc;
+
        /* V4L */
        struct saa7164_encoder_params encoder_params;
        struct video_device *v4l_device;