diff options
author | Thomas White <taw@bitwiz.org.uk> | 2009-11-10 00:06:38 +0100 |
---|---|---|
committer | Thomas White <taw@bitwiz.org.uk> | 2009-11-15 14:24:28 +0100 |
commit | c492dbbc420549a71cce8b0a8aea48c4a2c0e774 (patch) | |
tree | 31dda3bce39b6c92a3103b4e8578f134ff355f11 | |
parent | c848d00bd43c47e7f11724330380e0c68ec7ae5e (diff) |
Add interrupt-driven waitqueue for better GPU synchronisation
Signed-off-by: Thomas White <taw@bitwiz.org.uk>
-rw-r--r-- | drivers/mfd/glamo/Makefile | 2 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-buffer.c | 17 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-cmdq.c | 88 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-cmdq.h | 5 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-core.c | 23 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-core.h | 3 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-display.c | 3 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-drm-drv.c | 37 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-drm-private.h | 16 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-fence.c | 352 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-fence.h | 34 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-kms-fb.c | 2 | ||||
-rw-r--r-- | drivers/mfd/glamo/glamo-regs.h | 11 |
13 files changed, 498 insertions, 95 deletions
diff --git a/drivers/mfd/glamo/Makefile b/drivers/mfd/glamo/Makefile index 155427c5c61..fb37517fcaf 100644 --- a/drivers/mfd/glamo/Makefile +++ b/drivers/mfd/glamo/Makefile @@ -13,4 +13,4 @@ obj-$(CONFIG_MFD_GLAMO_MCI) += glamo-mci.o obj-$(CONFIG_MFD_GLAMO_DRM) += glamo-drm.o glamo-drm-objs := glamo-drm-drv.o glamo-cmdq.o glamo-buffer.o \ - glamo-display.o glamo-kms-fb.o + glamo-display.o glamo-kms-fb.o glamo-fence.o diff --git a/drivers/mfd/glamo/glamo-buffer.c b/drivers/mfd/glamo/glamo-buffer.c index a6d085f3303..2bf9fb538cd 100644 --- a/drivers/mfd/glamo/glamo-buffer.c +++ b/drivers/mfd/glamo/glamo-buffer.c @@ -151,8 +151,8 @@ int glamodrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) PAGE_SHIFT; mutex_lock(&dev->struct_mutex); - pfn = ((gdrm->vram->start + gobj->block->start) >> PAGE_SHIFT) - + page_offset; + pfn = ((gdrm->vram->start + GLAMO_OFFSET_FB + gobj->block->start) + >> PAGE_SHIFT) + page_offset; ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); mutex_unlock(&dev->struct_mutex); @@ -346,6 +346,19 @@ int glamo_buffer_init(struct glamodrm_handle *gdrm) { gdrm->mmgr = drm_calloc(1, sizeof(struct drm_mm), DRM_MEM_DRIVER); drm_mm_init(gdrm->mmgr, 0, gdrm->vram_size); + + /* Reserve a scratch buffer. We do this outside the protections + * of the other GEM code. To do this safely, the allocation must + * be a multiple of PAGE_SIZE. */ + gdrm->scratch = drm_mm_search_free(gdrm->mmgr, PAGE_SIZE, 4, 1); + if ( gdrm->scratch ) { + gdrm->scratch = drm_mm_get_block(gdrm->scratch, PAGE_SIZE, 4); + } + if ( !gdrm->scratch ) { + printk(KERN_WARNING "[glamo-drm] Couldn't allocate" + " scratch buffer!\n"); + } + return 0; } diff --git a/drivers/mfd/glamo/glamo-cmdq.c b/drivers/mfd/glamo/glamo-cmdq.c index 215017d86ba..f3dcc83b00f 100644 --- a/drivers/mfd/glamo/glamo-cmdq.c +++ b/drivers/mfd/glamo/glamo-cmdq.c @@ -51,8 +51,6 @@ */ -#include <linux/irq.h> -#include <linux/interrupt.h> #include <drm/drmP.h> #include <drm/glamo_drm.h> @@ -93,34 +91,9 @@ static u32 glamo_get_write(struct glamodrm_handle *gdrm) } -static void glamo_enable_cmdq_irq(struct glamodrm_handle *gdrm) -{ - uint16_t irq_status = reg_read(gdrm, GLAMO_REG_IRQ_ENABLE); - irq_status |= GLAMO_IRQ_CMDQUEUE; - reg_write(gdrm, GLAMO_REG_IRQ_ENABLE, irq_status); -} - - -static void glamo_set_cmdq_irq(struct glamodrm_handle *gdrm) -{ - uint16_t irq_status = reg_read(gdrm, GLAMO_REG_IRQ_SET); - irq_status |= GLAMO_IRQ_CMDQUEUE; - reg_write(gdrm, GLAMO_REG_IRQ_SET, irq_status); -} - - -static void glamo_cmdq_irq(unsigned int irq, struct irq_desc *desc) -{ - struct glamodrm_handle *gdrm = desc->handler_data; - - if (!gdrm) return; - reg_write(gdrm, GLAMO_REG_IRQ_CLEAR, GLAMO_IRQ_CMDQUEUE); -} - - /* Add commands to the ring buffer */ -static int glamo_add_to_ring(struct glamodrm_handle *gdrm, u16 *addr, - unsigned int count) +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; @@ -323,8 +296,6 @@ int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data, glamo_add_to_ring(gdrm, cmds, count); - glamo_set_cmdq_irq(gdrm); - cleanup: drm_free(cmds, 1, DRM_MEM_DRIVER); @@ -472,7 +443,6 @@ int glamo_ioctl_cmdburst(struct drm_device *dev, void *data, /* Add to command queue */ glamo_add_to_ring(gdrm, burst, burst_size); - glamo_set_cmdq_irq(gdrm); cleanup: drm_free(burst, 1, DRM_MEM_DRIVER); @@ -481,53 +451,6 @@ cleanup: } -/* TODO: Banish this to the nether regions of Hades */ -static void glamo_cmdq_wait(struct glamodrm_handle *gdrm, - enum glamo_engine engine) -{ - u16 mask, val, status; - int i; - - switch (engine) - { - case GLAMO_ENGINE_ALL: - mask = 1 << 2; - val = mask; - break; - default: - return; - } - - for ( i=0; i<1000; i++ ) { - status = reg_read(gdrm, GLAMO_REG_CMDQ_STATUS); - if ((status & mask) == val) break; - mdelay(1); - } - if ( i == 1000 ) { - size_t ring_read; - printk(KERN_WARNING "[glamo-drm] CmdQ timeout!\n"); - printk(KERN_WARNING "[glamo-drm] status = %x\n", status); - ring_read = reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRL); - ring_read |= ((reg_read(gdrm, GLAMO_REG_CMDQ_READ_ADDRH) - & 0x7) << 16); - printk(KERN_INFO "[glamo-drm] ring_read now 0x%x\n", - ring_read); - } -} - - -int glamo_ioctl_gem_wait_rendering(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct glamodrm_handle *gdrm; - - gdrm = dev->dev_private; - glamo_cmdq_wait(gdrm, GLAMO_ENGINE_ALL); - - return 0; -} - - int glamo_cmdq_init(struct glamodrm_handle *gdrm) { unsigned int i; @@ -561,19 +484,12 @@ int glamo_cmdq_init(struct glamodrm_handle *gdrm) 5 << 8 | /* no interrupt */ 8 << 4); /* HQ threshold */ - /* Set up 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); - return 0; } int glamo_cmdq_shutdown(struct glamodrm_handle *gdrm) { - set_irq_handler(IRQ_GLAMO(GLAMO_IRQIDX_CMDQUEUE), handle_level_irq); return 0; } diff --git a/drivers/mfd/glamo/glamo-cmdq.h b/drivers/mfd/glamo/glamo-cmdq.h index d5a00e6ce7d..510d1954c64 100644 --- a/drivers/mfd/glamo/glamo-cmdq.h +++ b/drivers/mfd/glamo/glamo-cmdq.h @@ -35,8 +35,6 @@ extern int glamo_ioctl_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int glamo_ioctl_cmdburst(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int glamo_ioctl_gem_wait_rendering(struct drm_device *dev, void *data, - struct drm_file *file_priv); extern void glamo_cmdq_blank(struct glamodrm_handle *gdrm, struct drm_gem_object *obj); @@ -45,4 +43,7 @@ extern int glamo_cmdq_shutdown(struct glamodrm_handle *gdrm); extern void glamo_cmdq_suspend(struct glamodrm_handle *gdrm); extern void glamo_cmdq_resume(struct glamodrm_handle *gdrm); +extern int glamo_add_to_ring(struct glamodrm_handle *gdrm, u16 *addr, + unsigned int count); + #endif /* __GLAMO_CMDQ_H */ diff --git a/drivers/mfd/glamo/glamo-core.c b/drivers/mfd/glamo/glamo-core.c index d8ebf400180..3dcb3589084 100644 --- a/drivers/mfd/glamo/glamo-core.c +++ b/drivers/mfd/glamo/glamo-core.c @@ -198,6 +198,11 @@ static struct resource glamo_graphics_resources[] = { .start = GLAMO_REGOFS_LCD, .end = GLAMO_REGOFS_MMC - 1, .flags = IORESOURCE_MEM, + }, { + .name = "glamo-2d-regs", + .start = GLAMO_REGOFS_2D, + .end = GLAMO_REGOFS_3D- 1, + .flags = IORESOURCE_MEM, } }; @@ -349,6 +354,24 @@ static void glamo_irq_demux_handler(unsigned int irq, struct irq_desc *desc) } +void glamo_clear_irq(struct glamo_core *glamo, unsigned int irq) +{ + /* set interrupt source */ + __reg_write(glamo, GLAMO_REG_IRQ_CLEAR, irq); +} + + +void glamo_enable_irq(struct glamo_core *glamo, unsigned int irq) +{ + u_int16_t tmp; + + /* set bit in enable register */ + tmp = __reg_read(glamo, GLAMO_REG_IRQ_ENABLE); + tmp |= irq; + __reg_write(glamo, GLAMO_REG_IRQ_ENABLE, tmp); +} + + static ssize_t regs_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { diff --git a/drivers/mfd/glamo/glamo-core.h b/drivers/mfd/glamo/glamo-core.h index 4808ad72bb4..1fe16df2642 100644 --- a/drivers/mfd/glamo/glamo-core.h +++ b/drivers/mfd/glamo/glamo-core.h @@ -100,6 +100,9 @@ void glamo_engine_reset(struct glamo_core *glamo, enum glamo_engine engine); int glamo_engine_reclock(struct glamo_core *glamo, enum glamo_engine engine, int ps); +extern void glamo_clear_irq(struct glamo_core *glamo, unsigned int irq); +extern void glamo_enable_irq(struct glamo_core *glamo, unsigned int irq); + void glamo_engine_clkreg_set(struct glamo_core *glamo, enum glamo_engine engine, u_int16_t mask, u_int16_t val); diff --git a/drivers/mfd/glamo/glamo-display.c b/drivers/mfd/glamo/glamo-display.c index 4b91a9a7562..f3bab3a4e8d 100644 --- a/drivers/mfd/glamo/glamo-display.c +++ b/drivers/mfd/glamo/glamo-display.c @@ -214,7 +214,8 @@ static struct glamo_script lcd_init_script[] = { * np cpu if, 9bit serial data, sclk rising edge latch data * 01 00 0 100 0 000 01 0 0 */ /* The following values assume 640*480@16bpp */ - { GLAMO_REG_LCD_A_BASE1, 0x0000 }, /* display A base address 15:0 */ + /* FIXME: fb0 has not yet been allocated! */ + { GLAMO_REG_LCD_A_BASE1, PAGE_SIZE }, /* display A base address 15:0 */ { GLAMO_REG_LCD_A_BASE2, 0x0000 }, /* display A base address 22:16 */ { GLAMO_REG_LCD_B_BASE1, 0x6000 }, /* display B base address 15:0 */ { GLAMO_REG_LCD_B_BASE2, 0x0009 }, /* display B base address 22:16 */ diff --git a/drivers/mfd/glamo/glamo-drm-drv.c b/drivers/mfd/glamo/glamo-drm-drv.c index c0c5c053268..6b78ea726aa 100644 --- a/drivers/mfd/glamo/glamo-drm-drv.c +++ b/drivers/mfd/glamo/glamo-drm-drv.c @@ -35,6 +35,7 @@ #include "glamo-drm-private.h" #include "glamo-display.h" #include "glamo-kms-fb.h" +#include "glamo-fence.h" #define DRIVER_AUTHOR "Openmoko, Inc." #define DRIVER_NAME "glamo-drm" @@ -72,7 +73,7 @@ struct drm_ioctl_desc glamo_ioctls[] = { DRM_IOCTL_DEF(DRM_GLAMO_GEM_PREAD, glamo_ioctl_gem_pread, DRM_AUTH), DRM_IOCTL_DEF(DRM_GLAMO_GEM_PWRITE, glamo_ioctl_gem_pwrite, DRM_AUTH), DRM_IOCTL_DEF(DRM_GLAMO_GEM_WAIT_RENDERING, - glamo_ioctl_gem_wait_rendering, DRM_AUTH), + glamo_ioctl_wait_rendering, DRM_AUTH), }; @@ -130,6 +131,7 @@ static int glamodrm_load(struct drm_device *dev, unsigned long flags) glamo_buffer_init(gdrm); glamo_cmdq_init(gdrm); + glamo_fence_init(gdrm); glamo_display_init(dev); return 0; @@ -145,6 +147,7 @@ static int glamodrm_unload(struct drm_device *dev) glamo_engine_disable(gdrm->glamo_core, GLAMO_ENGINE_2D); glamo_engine_disable(gdrm->glamo_core, GLAMO_ENGINE_3D); glamo_buffer_final(gdrm); + glamo_fence_shutdown(gdrm); return 0; } @@ -285,6 +288,28 @@ static int glamodrm_probe(struct platform_device *pdev) goto out_release_lcd; } + /* Find the 2D engine */ + gdrm->twod_regs = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if ( !gdrm->twod_regs ) { + dev_err(&pdev->dev, "Unable to find 2D registers.\n"); + rc = -ENOENT; + goto out_unmap_lcd; + } + gdrm->twod_regs = request_mem_region(gdrm->twod_regs->start, + RESSIZE(gdrm->twod_regs), + pdev->name); + if ( !gdrm->twod_regs ) { + dev_err(&pdev->dev, "failed to request 2D registers\n"); + rc = -ENOENT; + goto out_unmap_lcd; + } + gdrm->twod_base = ioremap(gdrm->twod_regs->start, RESSIZE(gdrm->twod_regs)); + if ( !gdrm->twod_base ) { + dev_err(&pdev->dev, "failed to ioremap() 2D registers\n"); + rc = -ENOENT; + goto out_release_2d; + } + gdrm->vram_size = GLAMO_FB_SIZE; printk(KERN_INFO "[glamo-drm] %lli bytes of VRAM\n", (long long int)gdrm->vram_size); @@ -294,6 +319,10 @@ static int glamodrm_probe(struct platform_device *pdev) return 0; +out_release_2d: + release_mem_region(gdrm->twod_regs->start, RESSIZE(gdrm->twod_regs)); +out_unmap_lcd: + iounmap(gdrm->lcd_base); out_release_lcd: release_mem_region(gdrm->lcd_regs->start, RESSIZE(gdrm->lcd_regs)); out_unmap_cmdq: @@ -332,10 +361,14 @@ static int glamodrm_remove(struct platform_device *pdev) // iounmap(gdrm->vram_base); release_mem_region(gdrm->vram->start, RESSIZE(gdrm->vram)); - /* Release command queue */ + /* Release command queue */ iounmap(gdrm->cmdq_base); release_mem_region(gdrm->cmdq->start, RESSIZE(gdrm->cmdq)); + /* Release 2D engine */ + iounmap(gdrm->twod_base); + release_mem_region(gdrm->twod_regs->start, RESSIZE(gdrm->twod_regs)); + kfree(gdrm); return 0; diff --git a/drivers/mfd/glamo/glamo-drm-private.h b/drivers/mfd/glamo/glamo-drm-private.h index 7b6ae21f6d1..02ae7e9fd2e 100644 --- a/drivers/mfd/glamo/glamo-drm-private.h +++ b/drivers/mfd/glamo/glamo-drm-private.h @@ -31,6 +31,8 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/semaphore.h> +#include <linux/spinlock.h> +#include <linux/wait.h> #include "glamo-core.h" @@ -66,6 +68,10 @@ struct glamodrm_handle { struct resource *lcd_regs; char __iomem *lcd_base; + /* 2D engine registers */ + struct resource *twod_regs; + char __iomem *twod_base; + ssize_t vram_size; /* Memory management */ @@ -89,6 +95,16 @@ struct glamodrm_handle { u_int16_t saved_vrtren; u_int16_t saved_vdspst; u_int16_t saved_vdspen; + + /* Fencing */ + atomic_t curr_seq; /* The last used stamp number */ + struct list_head fence_list; /* List of active fences */ + rwlock_t fence_list_lock; /* Lock to protect fence_list */ + wait_queue_head_t fence_queue; /* Waitqueue */ + struct tasklet_struct fence_tl; /* Tasklet for fence IRQ */ + + /* A scratch block */ + struct drm_mm_node *scratch; }; diff --git a/drivers/mfd/glamo/glamo-fence.c b/drivers/mfd/glamo/glamo-fence.c new file mode 100644 index 00000000000..2222b230278 --- /dev/null +++ b/drivers/mfd/glamo/glamo-fence.c @@ -0,0 +1,352 @@ +/* + * SMedia Glamo 336x/337x fence objects + * + * Copyright (c) 2009 Thomas White <taw@bitwiz.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * Loosely based on radeon_fence.c, to which the following notice applies: + * + * Copyright 2009 Jerome Glisse. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: + * Jerome Glisse <glisse@freedesktop.org> + * Dave Airlie + */ + + +#include <drm/drmP.h> +#include <drm/glamo_drm.h> +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/lockdep.h> + +#include "glamo-drm-private.h" +#include "glamo-regs.h" +#include "glamo-core.h" +#include "glamo-cmdq.h" + + +static struct lock_class_key glamo_fence_lock_key; + + +struct glamo_fence +{ + struct list_head list; + uint16_t seq; /* Wait for at least this ID */ + int signalled; /* Non-zero when fence has passed */ + struct glamodrm_handle *gdrm; +}; + + +static void glamo_fence_emit(struct glamo_fence *fence) +{ + u16 fring[26]; + u32 addr; + + addr = GLAMO_OFFSET_FB + fence->gdrm->scratch->start; + + fring[0] = 0x8000 | GLAMO_REG_2D_DST_X; + fring[1] = 11; + fring[2] = 0; /* dest X */ + fring[3] = 0; /* dest Y */ + fring[4] = addr & 0xffff; /* dest buffer */ + fring[5] = (addr >> 16) & 0x7f; /* dest buffer */ + fring[6] = 1; /* dest pitch */ + fring[7] = 1; /* dest height */ + fring[8] = 1; /* rect width */ + fring[9] = 1; /* rect height */ + fring[10] = 0x0000; /* patt L */ + fring[11] = 0x0000; /* patt H */ + fring[12] = 0x0000; /* FG colour */ + fring[13] = 0x0000; /* Padding */ + + fring[14] = 0x8000 | GLAMO_REG_2D_COMMAND1; + fring[15] = 3; + fring[16] = 0x0000; + fring[17] = 0xf0 << 8; + fring[18] = 0x0000; + fring[19] = 0x0000; /* Padding */ + + fring[20] = 0x8000 | GLAMO_REG_2D_ID1; + fring[21] = 3; + fence->seq = atomic_inc_return(&fence->gdrm->curr_seq); + if ( fence->seq > 1<<14 ) { + atomic_set(&fence->gdrm->curr_seq, 0); + fence->seq = atomic_inc_return(&fence->gdrm->curr_seq); + } + fring[22] = 1<<15 | fence->seq; + fring[23] = 0; /* Unused */ + fring[24] = 0; /* Unused */ + fring[25] = 0; /* Padding */ + + glamo_add_to_ring(fence->gdrm, fring, 52); +} + + +static void glamo_fence_enable(struct glamodrm_handle *gdrm) +{ + glamo_enable_irq(gdrm->glamo_core, GLAMO_IRQ_2D); +} + + +static inline u16 reg_read_2d(struct glamodrm_handle *gdrm, u_int16_t reg) +{ + /* For command queue, the address is given relative to + * the overall base of Glamo. This isn't the case here. */ + return ioread16(gdrm->twod_base + reg-GLAMO_REGOFS_2D); +} + + +static inline u16 reg_read_cmdq(struct glamodrm_handle *gdrm, u_int16_t reg) +{ + return ioread16(gdrm->reg_base + reg); +} + + +static void glamo_cmdq_wait(struct glamodrm_handle *gdrm, + enum glamo_engine engine) +{ + u16 mask, val, status; + int i; + + switch (engine) + { + case GLAMO_ENGINE_ALL: + mask = 1 << 2; + val = mask; + break; + default: + return; + } + + for ( i=0; i<1000; i++ ) { + status = reg_read_cmdq(gdrm, GLAMO_REG_CMDQ_STATUS); + if ((status & mask) == val) break; + mdelay(1); + } + if ( i == 1000 ) { + size_t ring_read; + printk(KERN_WARNING "[glamo-drm] CmdQ timeout!\n"); + printk(KERN_WARNING "[glamo-drm] status = %x\n", status); + ring_read = reg_read_cmdq(gdrm, GLAMO_REG_CMDQ_READ_ADDRL); + ring_read |= ((reg_read_cmdq(gdrm, GLAMO_REG_CMDQ_READ_ADDRH) + & 0x7) << 16); + printk(KERN_INFO "[glamo-drm] ring_read now 0x%x\n", + ring_read); + } +} + + +static void glamo_fence_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct glamodrm_handle *gdrm = desc->handler_data; + + if (!gdrm) { + printk(KERN_ERR "[glamo-drm] 2D IRQ called with no data\n"); + return; + } + glamo_clear_irq(gdrm->glamo_core, GLAMO_IRQ_2D); + + tasklet_schedule(&gdrm->fence_tl); +} + + +/* This is nasty. I'm sorry. */ +static void glamo_fence_debodge(struct glamodrm_handle *gdrm) +{ + struct list_head *tmp; + + printk(KERN_ERR "[glamo-drm] Attempting to recover...\n"); + + glamo_cmdq_wait(gdrm, GLAMO_ENGINE_ALL); + glamo_engine_reset(gdrm->glamo_core, GLAMO_ENGINE_2D); + + read_lock(&gdrm->fence_list_lock); + list_for_each(tmp, &gdrm->fence_list) { + + struct glamo_fence *fence; + + fence = list_entry(tmp, struct glamo_fence, list); + + if ( fence->signalled != 1 ) { + printk(KERN_ERR "[glamo-drm] Fence seq#%i was not" + " signalled\n", fence->seq); + } + fence->signalled = 1; + + } + read_unlock(&gdrm->fence_list_lock); + + wake_up_all(&gdrm->fence_queue); +} + + +static void glamo_fence_tl(unsigned long data) +{ + struct glamodrm_handle *gdrm = (struct glamodrm_handle *)data; + int wake = 0; + u16 seq; + struct list_head *tmp; + + + seq = reg_read_2d(gdrm, GLAMO_REG_2D_ID1) & 0x7fff; + + read_lock(&gdrm->fence_list_lock); + list_for_each(tmp, &gdrm->fence_list) { + + struct glamo_fence *fence; + + fence = list_entry(tmp, struct glamo_fence, list); + if ( seq >= fence->seq ) { + fence->signalled = 1; + wake = 1; + } + + } + read_unlock(&gdrm->fence_list_lock); + + if ( wake ) wake_up_all(&gdrm->fence_queue); +} + + +static struct glamo_fence *glamo_fence_new(struct glamodrm_handle *gdrm) +{ + struct glamo_fence *fence; + unsigned long irq_flags; + + fence = drm_calloc(1, sizeof(*fence), DRM_MEM_DRIVER); + fence->signalled = 0; + fence->gdrm = gdrm; + + /* Add to list */ + write_lock_irqsave(&gdrm->fence_list_lock, irq_flags); + list_add(&fence->list, &gdrm->fence_list); + write_unlock_irqrestore(&gdrm->fence_list_lock, irq_flags); + + return fence; +} + + +static struct glamo_fence *glamo_fence_destroy(struct glamo_fence *fence) +{ + unsigned long irq_flags; + struct glamodrm_handle *gdrm = fence->gdrm; + + /* Remove from list */ + write_lock_irqsave(&gdrm->fence_list_lock, irq_flags); + list_del(&fence->list); + write_unlock_irqrestore(&gdrm->fence_list_lock, irq_flags); + + drm_free(fence, 1, DRM_MEM_DRIVER); + + return fence; +} + + +int glamo_ioctl_wait_rendering(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct glamodrm_handle *gdrm; + struct drm_glamo_gem_wait_rendering *args = data; + struct glamo_fence *fence; + int r; + + gdrm = dev->dev_private; + + if ( !args->have_handle ) { + glamo_cmdq_wait(gdrm, GLAMO_ENGINE_ALL); + return 0; + } + + fence = glamo_fence_new(gdrm); + if ( fence == NULL ) { + printk(KERN_WARNING "[glamo-drm] Couldn't allocate fence -" + " falling back to busy wait.\n"); + glamo_cmdq_wait(gdrm, GLAMO_ENGINE_ALL); + return 0; + } + + glamo_fence_emit(fence); + + /* Wait... */ + r = wait_event_interruptible_timeout(gdrm->fence_queue, + fence->signalled, HZ); + if ( r == 0 ) { + printk(KERN_ERR "[glamo-drm] Timeout!\n"); + glamo_fence_debodge(gdrm); + } + + glamo_fence_destroy(fence); + + return 0; +} + + +void glamo_fence_init(struct glamodrm_handle *gdrm) +{ + unsigned long irq_flags; + + gdrm->fence_list_lock = __RW_LOCK_UNLOCKED(gdrm->fence_list_lock); + lockdep_set_class(&gdrm->fence_list_lock, &glamo_fence_lock_key); + init_waitqueue_head(&gdrm->fence_queue); + + atomic_set(&gdrm->curr_seq, 0); + + write_lock_irqsave(&gdrm->fence_list_lock, irq_flags); + INIT_LIST_HEAD(&gdrm->fence_list); + write_unlock_irqrestore(&gdrm->fence_list_lock, irq_flags); + + tasklet_init(&gdrm->fence_tl, glamo_fence_tl, (unsigned long)gdrm); + + /* Set up IRQ */ + set_irq_handler(IRQ_GLAMO(GLAMO_IRQIDX_2D), glamo_fence_irq_handler); + set_irq_data(IRQ_GLAMO(GLAMO_IRQIDX_2D), gdrm); + + glamo_fence_enable(gdrm); +} + + +void glamo_fence_shutdown(struct glamodrm_handle *gdrm) +{ + set_irq_handler(IRQ_GLAMO(GLAMO_IRQIDX_CMDQUEUE), handle_level_irq); + wake_up_all(&gdrm->fence_queue); + tasklet_kill(&gdrm->fence_tl); +} diff --git a/drivers/mfd/glamo/glamo-fence.h b/drivers/mfd/glamo/glamo-fence.h new file mode 100644 index 00000000000..deda9956f40 --- /dev/null +++ b/drivers/mfd/glamo/glamo-fence.h @@ -0,0 +1,34 @@ +/* + * SMedia Glamo 336x/337x fence objects + * + * Copyright (c) 2009 Thomas White <taw@bitwiz.org.uk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __GLAMO_FENCE_H +#define __GLAMO_FENCE_H + +#include <drm/drmP.h> + +#include "glamo-drm-private.h" + +extern void glamo_fence_init(struct glamodrm_handle *gdrm); +extern void glamo_fence_shutdown(struct glamodrm_handle *gdrm); + +extern int glamo_ioctl_wait_rendering(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +#endif /* __GLAMO_FENCE_H */ diff --git a/drivers/mfd/glamo/glamo-kms-fb.c b/drivers/mfd/glamo/glamo-kms-fb.c index 8fdfb874327..61cd6054eb8 100644 --- a/drivers/mfd/glamo/glamo-kms-fb.c +++ b/drivers/mfd/glamo/glamo-kms-fb.c @@ -436,7 +436,7 @@ int glamofb_create(struct drm_device *dev, uint32_t fb_width, info->flags = FBINFO_DEFAULT; offs = gobj->block->start; - info->screen_base = ioremap(gdrm->vram->start + offs, + info->screen_base = ioremap(gdrm->vram->start + offs + GLAMO_OFFSET_FB, GLAMO_FRAMEBUFFER_ALLOCATION); if (!info->screen_base) { printk(KERN_ERR "[glamo-drm] Couldn't map framebuffer!\n"); diff --git a/drivers/mfd/glamo/glamo-regs.h b/drivers/mfd/glamo/glamo-regs.h index 738cb64369b..7876cdff1fe 100644 --- a/drivers/mfd/glamo/glamo-regs.h +++ b/drivers/mfd/glamo/glamo-regs.h @@ -642,4 +642,15 @@ enum glamo_register_cq { GLAMO_REG_CMDQ_STATUS = 0x12, }; +#define REG_2D(x) (GLAMO_REGOFS_2D+(x)) + +enum glamo_register_2d { + GLAMO_REG_2D_DST_X = REG_2D(0x0a), + GLAMO_REG_2D_COMMAND1 = REG_2D(0x3a), + GLAMO_REG_2D_STATUS = REG_2D(0x42), + GLAMO_REG_2D_ID1 = REG_2D(0x44), + GLAMO_REG_2D_ID2 = REG_2D(0x46), + GLAMO_REG_2D_ID3 = REG_2D(0x48), +}; + #endif /* _GLAMO_REGS_H */ |