How to use Glamo interrupts

Michael Trimarchi trimarchi at gandalf.sssup.it
Sat Jun 6 10:09:38 CEST 2009


Andreas Pokorny wrote:
> 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);
>   
Check if the interrupt is what you expected read the IRQ_STATUS and 
adding some
debug code here. (Is it possible to use the request_irq on the glamo_irq 
chip?)
The IRQ_STATUS give the info if some interrupt arrived.
What happen if you ack this irq and you take no lock on cmdq_irq, then 
arrive another
one and you don't mask and mark it pending.

> 	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);
>   
I suppose that here you will do an unmask of the interrupt.
> 	/* 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);
>   
The interrupt is not just umask? So way unmask it again?
If you want and interrupt at each new command in the buffer why you 
don't mask
it and ack?
> 	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
>   
Michael
>
>   




More information about the openmoko-kernel mailing list