aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-3.10/0149-bcm2708_fb-use-IRQ-for-DMA-copies.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-3.10/0149-bcm2708_fb-use-IRQ-for-DMA-copies.patch')
-rw-r--r--target/linux/brcm2708/patches-3.10/0149-bcm2708_fb-use-IRQ-for-DMA-copies.patch206
1 files changed, 206 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.10/0149-bcm2708_fb-use-IRQ-for-DMA-copies.patch b/target/linux/brcm2708/patches-3.10/0149-bcm2708_fb-use-IRQ-for-DMA-copies.patch
new file mode 100644
index 0000000000..bf915beb80
--- /dev/null
+++ b/target/linux/brcm2708/patches-3.10/0149-bcm2708_fb-use-IRQ-for-DMA-copies.patch
@@ -0,0 +1,206 @@
+From ab41011a32fdc35352ee6b308f29b6954056d787 Mon Sep 17 00:00:00 2001
+From: Luke Diamand <luked@broadcom.com>
+Date: Wed, 1 Jan 2014 00:45:29 +0000
+Subject: [PATCH 149/174] bcm2708_fb: use IRQ for DMA copies
+
+The copyarea ioctl() uses DMA to speed things along. This
+was busy-waiting for completion. This change supports using
+an interrupt instead for larger transfers. For small
+transfers, busy-waiting is still likely to be faster.
+
+Signed-off-by: Luke Diamand <luke@diamand.org>
+---
+ arch/arm/mach-bcm2708/dma.c | 8 ++++
+ arch/arm/mach-bcm2708/include/mach/dma.h | 2 +
+ drivers/video/bcm2708_fb.c | 64 ++++++++++++++++++++++++++++++--
+ 3 files changed, 70 insertions(+), 4 deletions(-)
+
+--- a/arch/arm/mach-bcm2708/dma.c
++++ b/arch/arm/mach-bcm2708/dma.c
+@@ -83,6 +83,14 @@ extern void bcm_dma_wait_idle(void __iom
+
+ EXPORT_SYMBOL_GPL(bcm_dma_start);
+
++extern bool bcm_dma_is_busy(void __iomem *dma_chan_base)
++{
++ dsb();
++
++ return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE;
++}
++EXPORT_SYMBOL_GPL(bcm_dma_is_busy);
++
+ /* Complete an ongoing DMA (assuming its results are to be ignored)
+ Does nothing if there is no DMA in progress.
+ This routine waits for the current AXI transfer to complete before
+--- a/arch/arm/mach-bcm2708/include/mach/dma.h
++++ b/arch/arm/mach-bcm2708/include/mach/dma.h
+@@ -64,11 +64,13 @@ struct bcm2708_dma_cb {
+ unsigned long next;
+ unsigned long pad[2];
+ };
++struct scatterlist;
+
+ extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len);
+ extern void bcm_dma_start(void __iomem *dma_chan_base,
+ dma_addr_t control_block);
+ extern void bcm_dma_wait_idle(void __iomem *dma_chan_base);
++extern bool bcm_dma_is_busy(void __iomem *dma_chan_base);
+ extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base);
+
+ /* When listing features we can ask for when allocating DMA channels give
+--- a/drivers/video/bcm2708_fb.c
++++ b/drivers/video/bcm2708_fb.c
+@@ -21,6 +21,7 @@
+ #include <linux/mm.h>
+ #include <linux/fb.h>
+ #include <linux/init.h>
++#include <linux/interrupt.h>
+ #include <linux/ioport.h>
+ #include <linux/list.h>
+ #include <linux/platform_device.h>
+@@ -48,6 +49,11 @@ static const char *bcm2708_name = "BCM27
+
+ #define DRIVER_NAME "bcm2708_fb"
+
++static u32 dma_busy_wait_threshold = 1<<15;
++module_param(dma_busy_wait_threshold, int, 0644);
++MODULE_PARM_DESC(dma_busy_wait_threshold, "Busy-wait for DMA completion below this area");
++
++
+ /* this data structure describes each frame buffer device we find */
+
+ struct fbinfo_s {
+@@ -77,6 +83,7 @@ struct bcm2708_fb {
+ void *cb_base; /* DMA control blocks */
+ dma_addr_t cb_handle;
+ struct dentry *debugfs_dir;
++ wait_queue_head_t dma_waitq;
+ struct bcm2708_fb_stats stats;
+ };
+
+@@ -95,6 +102,10 @@ static int bcm2708_fb_debugfs_init(struc
+ "dma_copies",
+ offsetof(struct bcm2708_fb_stats, dma_copies)
+ },
++ {
++ "dma_irqs",
++ offsetof(struct bcm2708_fb_stats, dma_irqs)
++ },
+ };
+
+ fb->debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
+@@ -400,6 +411,7 @@ static void bcm2708_fb_copyarea(struct f
+ int bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
+ /* Channel 0 supports larger bursts and is a bit faster */
+ int burst_size = (fb->dma_chan == 0) ? 8 : 2;
++ int pixels = region->width * region->height;
+
+ /* Fallback to cfb_copyarea() if we don't like something */
+ if (bytes_per_pixel > 4 ||
+@@ -492,8 +504,20 @@ static void bcm2708_fb_copyarea(struct f
+ cb->next = 0;
+
+
+- bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
+- bcm_dma_wait_idle(fb->dma_chan_base);
++ if (pixels < dma_busy_wait_threshold) {
++ bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
++ bcm_dma_wait_idle(fb->dma_chan_base);
++ } else {
++ void __iomem *dma_chan = fb->dma_chan_base;
++ cb->info |= BCM2708_DMA_INT_EN;
++ bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
++ while (bcm_dma_is_busy(dma_chan)) {
++ wait_event_interruptible(
++ fb->dma_waitq,
++ !bcm_dma_is_busy(dma_chan));
++ }
++ fb->stats.dma_irqs++;
++ }
+ fb->stats.dma_copies++;
+ }
+
+@@ -504,6 +528,24 @@ static void bcm2708_fb_imageblit(struct
+ cfb_imageblit(info, image);
+ }
+
++static irqreturn_t bcm2708_fb_dma_irq(int irq, void *cxt)
++{
++ struct bcm2708_fb *fb = cxt;
++
++ /* FIXME: should read status register to check if this is
++ * actually interrupting us or not, in case this interrupt
++ * ever becomes shared amongst several DMA channels
++ *
++ * readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_IRQ;
++ */
++
++ /* acknowledge the interrupt */
++ writel(BCM2708_DMA_INT, fb->dma_chan_base + BCM2708_DMA_CS);
++
++ wake_up(&fb->dma_waitq);
++ return IRQ_HANDLED;
++}
++
+ static struct fb_ops bcm2708_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = bcm2708_fb_check_var,
+@@ -568,6 +610,7 @@ static int bcm2708_fb_register(struct bc
+ fb->fb.monspecs.dclkmax = 100000000;
+
+ bcm2708_fb_set_bitfields(&fb->fb.var);
++ init_waitqueue_head(&fb->dma_waitq);
+
+ /*
+ * Allocate colourmap.
+@@ -593,14 +636,15 @@ static int bcm2708_fb_probe(struct platf
+ struct bcm2708_fb *fb;
+ int ret;
+
+- fb = kmalloc(sizeof(struct bcm2708_fb), GFP_KERNEL);
++ fb = kzalloc(sizeof(struct bcm2708_fb), GFP_KERNEL);
+ if (!fb) {
+ dev_err(&dev->dev,
+ "could not allocate new bcm2708_fb struct\n");
+ ret = -ENOMEM;
+ goto free_region;
+ }
+- memset(fb, 0, sizeof(struct bcm2708_fb));
++
++ bcm2708_fb_debugfs_init(fb);
+
+
+ bcm2708_fb_debugfs_init(fb);
+@@ -624,6 +668,14 @@ static int bcm2708_fb_probe(struct platf
+ }
+ fb->dma_chan = ret;
+
++ ret = request_irq(fb->dma_irq, bcm2708_fb_dma_irq,
++ 0, "bcm2708_fb dma", fb);
++ if (ret) {
++ pr_err("%s: failed to request DMA irq\n", __func__);
++ goto free_dma_chan;
++ }
++
++
+ pr_info("BCM2708FB: allocated DMA channel %d @ %p\n",
+ fb->dma_chan, fb->dma_chan_base);
+
+@@ -635,6 +687,8 @@ static int bcm2708_fb_probe(struct platf
+ goto out;
+ }
+
++free_dma_chan:
++ bcm_dma_chan_free(fb->dma_chan);
+ free_cb:
+ dma_free_writecombine(&dev->dev, SZ_64K, fb->cb_base, fb->cb_handle);
+ free_fb:
+@@ -662,6 +716,8 @@ static int bcm2708_fb_remove(struct plat
+ fb->dma);
+ bcm2708_fb_debugfs_deinit(fb);
+
++ free_irq(fb->dma_irq, fb);
++
+ kfree(fb);
+
+ return 0;