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



Reply via email to