diff options
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.patch | 206 |
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; |