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

Reply via email to