How to use Glamo interrupts

Andreas Pokorny andreas.pokorny at gmail.com
Fri Jun 5 14:19:46 CEST 2009


Hello,

Here is some code - It tries to avoid the waits for the glamo chip by
writing the write pointer of the SQ during an SQ idle interrupt.:

static void glamo_cmdq_irq(unsigned int irq, struct irq_desc *desc)
{
	unsigned long flags;
	struct glamodrm_handle * gdrm = desc->handler_data;
	ssize_t new_ring_write;

	if(!gdrm)
		return;

	/* ack the interrupt source */
	reg_write(gdrm, GLAMO_REG_IRQ_CLEAR, GLAMO_IRQ_CMDQUEUE);

	spin_lock_irqsave(&gdrm->new_ring_write_lock, flags);
	new_ring_write = gdrm->new_ring_write;
	spin_unlock_irqrestore(&gdrm->new_ring_write_lock, flags);

	/* Note that CLOCK_2D_EN_M6CLK has nothing to do with the 2D engine */
	glamo_engine_clkreg_set(gdrm->glamo_core, GLAMO_ENGINE_2D,
				GLAMO_CLOCK_2D_EN_M6CLK, 0x0000);
	reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH,
					(new_ring_write >> 16) & 0x7f);
	reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL,
					new_ring_write & 0xffff);
	glamo_engine_clkreg_set(gdrm->glamo_core, GLAMO_ENGINE_2D,
				GLAMO_CLOCK_2D_EN_M6CLK, 0xffff);

	printk( KERN_INFO "[glamo-drm] Write Pointer: %d\n", new_ring_write);
}

On Init:
int glamo_cmdq_init(struct glamodrm_handle *gdrm)
{
	unsigned int i;

	init_MUTEX(&gdrm->add_to_ring);
	spin_lock_init(&gdrm->new_ring_write_lock);

	/* Enable 2D and 3D */
	glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_2D);
	glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_2D);

	/* Start by zeroing the command queue memory */
	for ( i=0; i<GLAMO_CMDQ_SIZE; i+=2 ) {
		iowrite16(0x0000, gdrm->cmdq_base+i);
	}

	glamo_engine_enable(gdrm->glamo_core, GLAMO_ENGINE_CMDQ);
	glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_CMDQ);

	/* Set up command queue location */
	reg_write(gdrm, GLAMO_REG_CMDQ_BASE_ADDRL,
					GLAMO_OFFSET_CMDQ & 0xffff);
	reg_write(gdrm, GLAMO_REG_CMDQ_BASE_ADDRH,
					(GLAMO_OFFSET_CMDQ >> 16) & 0x7f);


	/* setup irq */
	set_irq_handler(IRQ_GLAMO(GLAMO_IRQIDX_CMDQUEUE), glamo_cmdq_irq);
	set_irq_data(IRQ_GLAMO(GLAMO_IRQIDX_CMDQUEUE), gdrm);

	glamo_enable_cmdq_irq(gdrm);

	/* initial write position is 0 */
	gdrm->new_ring_write = 0;

	/* Length of command queue in 1k blocks, minus one */
	reg_write(gdrm, GLAMO_REG_CMDQ_LEN, (GLAMO_CMDQ_SIZE >> 10)-1);
	reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRH, 0);
	reg_write(gdrm, GLAMO_REG_CMDQ_WRITE_ADDRL, 0);
	reg_write(gdrm, GLAMO_REG_CMDQ_READ_ADDRH, 0);
	reg_write(gdrm, GLAMO_REG_CMDQ_READ_ADDRL, 0);
	reg_write(gdrm, GLAMO_REG_CMDQ_CONTROL,
					 1 << 12 |	/* Turbo flip (?) */
					 3 << 8 |	/* SQ Idle interrupt */
					 8 << 4);	/* HQ threshold */

	/* Wait for things to settle down */
	glamo_cmdq_wait(gdrm, GLAMO_ENGINE_ALL);

	return 0;
}

Then for every command buffer sent to drm:

/* Add commands to the ring buffer */
static int glamo_add_to_ring(struct glamodrm_handle *gdrm, u16 *addr,
			     unsigned int count)
{
	size_t ring_write, ring_read;
	size_t new_ring_write;
	unsigned long flags;

	up(&gdrm->add_to_ring);

	spin_lock_irqsave( &gdrm->new_ring_write_lock, flags );
	ring_write = gdrm->new_ring_write;
	spin_unlock_irqrestore( &gdrm->new_ring_write_lock, flags );

	/* Calculate where we'll end up */
	new_ring_write = (ring_write + count) % GLAMO_CMDQ_SIZE;

	/* Wait until there is enough space to queue the cmd buffer */
	if (new_ring_write > ring_write) {
		/* Loop while the read pointer is between the old and new
		 * positions */
		do {
			ring_read = reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRL);
			ring_read |= ((reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRH)
					& 0x7) << 16);
		} while (ring_read > ring_write && ring_read < new_ring_write);
	   	} else {
		/* Same, but kind of inside-out */
		do {
		   	ring_read = reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRL);
			ring_read |= ((reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRH)
					& 0x7) << 16);
		} while (ring_read > ring_write || ring_read < new_ring_write);
	}

	/* Are we about to wrap around? */
	if (ring_write >= new_ring_write) {

		size_t rest_size;
		int i;
		printk(KERN_INFO "[glamo-drm] CmdQ wrap-around...\n");
		/* Wrap around */
		rest_size = GLAMO_CMDQ_SIZE - ring_write; /* Space left */

		/* Write from current position to end */
		for ( i=0; i<rest_size; i++ ) {
			iowrite16(*(addr+i), gdrm->cmdq_base+ring_write+(i*2));
		}

		/* Write from start */
		for ( i=0; i<(count-rest_size); i++ ) {
			iowrite16(*(addr+rest_size+i), gdrm->cmdq_base+(i*2));
		}

		/* ring_write being 0 will result in a deadlock because the
		 * cmdq read will never stop. To avoid such an behaviour insert
		 * an empty instruction. */
		if (new_ring_write == 0) {
			iowrite16(0x0000, gdrm->cmdq_base);
			iowrite16(0x0000, gdrm->cmdq_base + 2);
			new_ring_write = 4;
		}

		/* Suppose we just filled the WHOLE ring buffer, and so the
		 * write position ends up in the same place as it started.
		 * No change in pointer means no activity from the command
		 * queue engine.  So, insert a no-op */
		if (ring_write == new_ring_write) {
			iowrite16(0x0000, gdrm->cmdq_base + new_ring_write);
			iowrite16(0x0000, gdrm->cmdq_base + new_ring_write + 2);
			new_ring_write += 4;
		}

	} else {

		int i;
		/* The easy case */
		for ( i=0; i<count/2; i++ ) { /* Number of words */
			iowrite16(*(addr+i), gdrm->cmdq_base+ring_write+(i*2));
		}

		/* this completes the command - if we do not add that
		 * the commands get executed in a loop */
		iowrite16(0x0000, gdrm->cmdq_base + new_ring_write);
		iowrite16(0x0000, gdrm->cmdq_base + new_ring_write + 2);
		new_ring_write += 4;

	}

	spin_lock_irqsave( &gdrm->new_ring_write_lock, flags );
	gdrm->new_ring_write = new_ring_write;
	spin_unlock_irqrestore( &gdrm->new_ring_write_lock, flags );

	/* We try to make the irq happen somehow :( */
	printk(KERN_INFO "[glamo-drm] enabling...\n");
	glamo_enable_cmdq_irq(gdrm);
	printk(KERN_INFO "[glamo-drm] writing CMDQ_CONTROL ...\n");
	reg_write(gdrm, GLAMO_REG_CMDQ_CONTROL,
					 1 << 12 |	/* Turbo flip (?) */
					 3 << 8 |	/* SQ Idle interrupt */
					 8 << 4);	/* HQ threshold */
	printk(KERN_INFO "[glamo-drm] ..expecting irq real soon now\n");

	down(&gdrm->add_to_ring);

	return 0;
}

The IRQ handler gets called only once, right after the first time i
send data to drm using the test application Thomas White wrote. Any
ideas?

kind regards
Andreas Pokorny



More information about the openmoko-kernel mailing list