Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91d80189 authored by Steven Toth's avatar Steven Toth Committed by Mauro Carvalho Chehab
Browse files

[media] saa7164: Implement encoder irq handling in a deferred work queue

parent 7615e434
Loading
Loading
Loading
Loading
+194 −29
Original line number Diff line number Diff line
@@ -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;
	}

static void saa7164_buffer_deliver(struct saa7164_buffer *buf)
	/* 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)
{
	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)
+23 −0
Original line number Diff line number Diff line
@@ -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;