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