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