[media] saa7164: Implement encoder irq handling in a deferred work queue
Signed-off-by: Steven Toth <stoth@kernellabs.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c
index b8e56d8..591a3c1 100644
--- a/drivers/media/video/saa7164/saa7164-core.c
+++ b/drivers/media/video/saa7164/saa7164-core.c
@@ -57,6 +57,10 @@
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,6 +68,164 @@
#define INT_SIZE 16
+static void saa7164_histogram_reset(struct saa7164_histogram *hg, char *name)
+{
+ int i;
+
+ 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_histogram_update(struct saa7164_histogram *hg, u32 val)
+{
+ 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;
+ }
+ }
+}
+
+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 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, rp, i = 0;
+
+ 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;
+
+
+ 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) {
+ printk(KERN_ERR "%s() illegal i count %d\n",
+ __func__, i);
+ break;
+ }
+
+ if (buf->idx == rp) {
+ /* Found the buffer, deal with it */
+ dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n",
+ __func__, wp, rp);
+
+ /* */
+ /* find a free user buffer and clone to it */
+ if (!list_empty(&port->list_buf_free.list)) {
+
+ /* Pull the first buffer from the used list */
+ ubuf = list_first_entry(&port->list_buf_free.list,
+ struct saa7164_user_buffer, list);
+
+ if (ubuf->actual_size == buf->actual_size)
+ memcpy(ubuf->data, buf->cpu,
+ ubuf->actual_size);
+
+ /* Requeue the buffer on the free list */
+ ubuf->pos = 0;
+
+ list_move_tail(&ubuf->list,
+ &port->list_buf_used.list);
+
+ /* Flag any userland waiters */
+ wake_up_interruptible(&port->wait_read);
+
+ } else
+ printk(KERN_ERR "encirq no free buffers\n");
+
+ break;
+ }
+
+ }
+ 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);
@@ -85,15 +247,14 @@
static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
{
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;
/* Find the current write point from the hardware */
wp = saa7164_readl(port->bufcounter);
- if (wp > (port->hwcfg.buffercount - 1))
- BUG();
+ 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)
@@ -101,47 +262,35 @@
else
rp = wp - 1;
- /* Lookup the WP in the buffer list */
- /* TODO: turn this into a worker thread */
- list_for_each_safe(c, n, &port->dmaqueue.list) {
- buf = list_entry(c, struct saa7164_buffer, list);
- if (i++ > port->hwcfg.buffercount)
- BUG();
-
- if (buf->idx == rp) {
- /* Found the buffer, deal with it */
- dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n",
- __func__, wp, rp);
-
- /* */
- /* find a free user buffer and clone to it */
- if (!list_empty(&port->list_buf_free.list)) {
-
- /* Pull the first buffer from the used list */
- ubuf = list_first_entry(&port->list_buf_free.list,
- struct saa7164_user_buffer, list);
-
- if (ubuf->actual_size == buf->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);
-
- /* Flag any userland waiters */
- wake_up_interruptible(&port->wait_read);
-
- } else
- printk(KERN_ERR "encirq no free buffers\n");
-
- break;
- }
-
+ 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 @@
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 @@
{
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)
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
index ad39072..a262875 100644
--- a/drivers/media/video/saa7164/saa7164.h
+++ b/drivers/media/video/saa7164/saa7164.h
@@ -176,6 +176,17 @@
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 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 @@
tmComResExtDevDescrHeader_t ifunit;
tmComResTunerDescrHeader_t tunerunit;
+ struct work_struct workenc;
+
/* V4L */
struct saa7164_encoder_params encoder_params;
struct video_device *v4l_device;