aboutsummaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
authorThomas White <taw@bitwiz.org.uk>2009-11-10 00:06:38 +0100
committerThomas White <taw@bitwiz.org.uk>2009-11-15 14:24:28 +0100
commitc492dbbc420549a71cce8b0a8aea48c4a2c0e774 (patch)
tree31dda3bce39b6c92a3103b4e8578f134ff355f11 /drivers/mfd
parentc848d00bd43c47e7f11724330380e0c68ec7ae5e (diff)
Add interrupt-driven waitqueue for better GPU synchronisation
Signed-off-by: Thomas White <taw@bitwiz.org.uk>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/glamo/Makefile2
-rw-r--r--drivers/mfd/glamo/glamo-buffer.c17
-rw-r--r--drivers/mfd/glamo/glamo-cmdq.c88
-rw-r--r--drivers/mfd/glamo/glamo-cmdq.h5
-rw-r--r--drivers/mfd/glamo/glamo-core.c23
-rw-r--r--drivers/mfd/glamo/glamo-core.h3
-rw-r--r--drivers/mfd/glamo/glamo-display.c3
-rw-r--r--drivers/mfd/glamo/glamo-drm-drv.c37
-rw-r--r--drivers/mfd/glamo/glamo-drm-private.h16
-rw-r--r--drivers/mfd/glamo/glamo-fence.c352
-rw-r--r--drivers/mfd/glamo/glamo-fence.h34
-rw-r--r--drivers/mfd/glamo/glamo-kms-fb.c2
-rw-r--r--drivers/mfd/glamo/glamo-regs.h11
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 */