brcm2708: update 3.10 patches with raspberrypi/rpi-3.10.y of 27 Apr. 2014
[openwrt.git] / target / linux / brcm2708 / patches-3.10 / 0149-bcm2708_fb-use-IRQ-for-DMA-copies.patch
1 From 298c1744068079c5174e0c6023a89cb5ad3576c4 Mon Sep 17 00:00:00 2001
2 From: Luke Diamand <luked@broadcom.com>
3 Date: Wed, 1 Jan 2014 00:45:29 +0000
4 Subject: [PATCH 149/196] bcm2708_fb: use IRQ for DMA copies
5
6 The copyarea ioctl() uses DMA to speed things along. This
7 was busy-waiting for completion. This change supports using
8 an interrupt instead for larger transfers. For small
9 transfers, busy-waiting is still likely to be faster.
10
11 Signed-off-by: Luke Diamand <luke@diamand.org>
12 ---
13  arch/arm/mach-bcm2708/dma.c              |  8 ++++
14  arch/arm/mach-bcm2708/include/mach/dma.h |  2 +
15  drivers/video/bcm2708_fb.c               | 64 ++++++++++++++++++++++++++++++--
16  3 files changed, 70 insertions(+), 4 deletions(-)
17
18 diff --git a/arch/arm/mach-bcm2708/dma.c b/arch/arm/mach-bcm2708/dma.c
19 index 51d147a..1da2413 100644
20 --- a/arch/arm/mach-bcm2708/dma.c
21 +++ b/arch/arm/mach-bcm2708/dma.c
22 @@ -83,6 +83,14 @@ extern void bcm_dma_wait_idle(void __iomem *dma_chan_base)
23  
24  EXPORT_SYMBOL_GPL(bcm_dma_start);
25  
26 +extern bool bcm_dma_is_busy(void __iomem *dma_chan_base)
27 +{
28 +       dsb();
29 +
30 +       return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE;
31 +}
32 +EXPORT_SYMBOL_GPL(bcm_dma_is_busy);
33 +
34  /* Complete an ongoing DMA (assuming its results are to be ignored)
35     Does nothing if there is no DMA in progress.
36     This routine waits for the current AXI transfer to complete before
37 diff --git a/arch/arm/mach-bcm2708/include/mach/dma.h b/arch/arm/mach-bcm2708/include/mach/dma.h
38 index f2568d4..a4aac4c 100644
39 --- a/arch/arm/mach-bcm2708/include/mach/dma.h
40 +++ b/arch/arm/mach-bcm2708/include/mach/dma.h
41 @@ -64,11 +64,13 @@ struct bcm2708_dma_cb {
42         unsigned long next;
43         unsigned long pad[2];
44  };
45 +struct scatterlist;
46  
47  extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len);
48  extern void bcm_dma_start(void __iomem *dma_chan_base,
49                           dma_addr_t control_block);
50  extern void bcm_dma_wait_idle(void __iomem *dma_chan_base);
51 +extern bool bcm_dma_is_busy(void __iomem *dma_chan_base);
52  extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base);
53  
54  /* When listing features we can ask for when allocating DMA channels give
55 diff --git a/drivers/video/bcm2708_fb.c b/drivers/video/bcm2708_fb.c
56 index 4d7d963..5758146 100644
57 --- a/drivers/video/bcm2708_fb.c
58 +++ b/drivers/video/bcm2708_fb.c
59 @@ -21,6 +21,7 @@
60  #include <linux/mm.h>
61  #include <linux/fb.h>
62  #include <linux/init.h>
63 +#include <linux/interrupt.h>
64  #include <linux/ioport.h>
65  #include <linux/list.h>
66  #include <linux/platform_device.h>
67 @@ -48,6 +49,11 @@ static const char *bcm2708_name = "BCM2708 FB";
68  
69  #define DRIVER_NAME "bcm2708_fb"
70  
71 +static u32 dma_busy_wait_threshold = 1<<15;
72 +module_param(dma_busy_wait_threshold, int, 0644);
73 +MODULE_PARM_DESC(dma_busy_wait_threshold, "Busy-wait for DMA completion below this area");
74 +
75 +
76  /* this data structure describes each frame buffer device we find */
77  
78  struct fbinfo_s {
79 @@ -77,6 +83,7 @@ struct bcm2708_fb {
80         void *cb_base;          /* DMA control blocks */
81         dma_addr_t cb_handle;
82         struct dentry *debugfs_dir;
83 +       wait_queue_head_t dma_waitq;
84         struct bcm2708_fb_stats stats;
85  };
86  
87 @@ -95,6 +102,10 @@ static int bcm2708_fb_debugfs_init(struct bcm2708_fb *fb)
88                         "dma_copies",
89                         offsetof(struct bcm2708_fb_stats, dma_copies)
90                 },
91 +               {
92 +                       "dma_irqs",
93 +                       offsetof(struct bcm2708_fb_stats, dma_irqs)
94 +               },
95         };
96  
97         fb->debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
98 @@ -400,6 +411,7 @@ static void bcm2708_fb_copyarea(struct fb_info *info,
99         int bytes_per_pixel = (info->var.bits_per_pixel + 7) >> 3;
100         /* Channel 0 supports larger bursts and is a bit faster */
101         int burst_size = (fb->dma_chan == 0) ? 8 : 2;
102 +       int pixels = region->width * region->height;
103  
104         /* Fallback to cfb_copyarea() if we don't like something */
105         if (bytes_per_pixel > 4 ||
106 @@ -492,8 +504,20 @@ static void bcm2708_fb_copyarea(struct fb_info *info,
107         cb->next = 0;
108  
109  
110 -       bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
111 -       bcm_dma_wait_idle(fb->dma_chan_base);
112 +       if (pixels < dma_busy_wait_threshold) {
113 +               bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
114 +               bcm_dma_wait_idle(fb->dma_chan_base);
115 +       } else {
116 +               void __iomem *dma_chan = fb->dma_chan_base;
117 +               cb->info |= BCM2708_DMA_INT_EN;
118 +               bcm_dma_start(fb->dma_chan_base, fb->cb_handle);
119 +               while (bcm_dma_is_busy(dma_chan)) {
120 +                       wait_event_interruptible(
121 +                               fb->dma_waitq,
122 +                               !bcm_dma_is_busy(dma_chan));
123 +               }
124 +               fb->stats.dma_irqs++;
125 +       }
126         fb->stats.dma_copies++;
127  }
128  
129 @@ -504,6 +528,24 @@ static void bcm2708_fb_imageblit(struct fb_info *info,
130         cfb_imageblit(info, image);
131  }
132  
133 +static irqreturn_t bcm2708_fb_dma_irq(int irq, void *cxt)
134 +{
135 +       struct bcm2708_fb *fb = cxt;
136 +
137 +       /* FIXME: should read status register to check if this is
138 +        * actually interrupting us or not, in case this interrupt
139 +        * ever becomes shared amongst several DMA channels
140 +        *
141 +        * readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_IRQ;
142 +        */
143 +
144 +       /* acknowledge the interrupt */
145 +       writel(BCM2708_DMA_INT, fb->dma_chan_base + BCM2708_DMA_CS);
146 +
147 +       wake_up(&fb->dma_waitq);
148 +       return IRQ_HANDLED;
149 +}
150 +
151  static struct fb_ops bcm2708_fb_ops = {
152         .owner = THIS_MODULE,
153         .fb_check_var = bcm2708_fb_check_var,
154 @@ -568,6 +610,7 @@ static int bcm2708_fb_register(struct bcm2708_fb *fb)
155         fb->fb.monspecs.dclkmax = 100000000;
156  
157         bcm2708_fb_set_bitfields(&fb->fb.var);
158 +       init_waitqueue_head(&fb->dma_waitq);
159  
160         /*
161          * Allocate colourmap.
162 @@ -593,14 +636,15 @@ static int bcm2708_fb_probe(struct platform_device *dev)
163         struct bcm2708_fb *fb;
164         int ret;
165  
166 -       fb = kmalloc(sizeof(struct bcm2708_fb), GFP_KERNEL);
167 +       fb = kzalloc(sizeof(struct bcm2708_fb), GFP_KERNEL);
168         if (!fb) {
169                 dev_err(&dev->dev,
170                         "could not allocate new bcm2708_fb struct\n");
171                 ret = -ENOMEM;
172                 goto free_region;
173         }
174 -       memset(fb, 0, sizeof(struct bcm2708_fb));
175 +
176 +       bcm2708_fb_debugfs_init(fb);
177  
178  
179         bcm2708_fb_debugfs_init(fb);
180 @@ -624,6 +668,14 @@ static int bcm2708_fb_probe(struct platform_device *dev)
181         }
182         fb->dma_chan = ret;
183  
184 +       ret = request_irq(fb->dma_irq, bcm2708_fb_dma_irq,
185 +                         0, "bcm2708_fb dma", fb);
186 +       if (ret) {
187 +               pr_err("%s: failed to request DMA irq\n", __func__);
188 +               goto free_dma_chan;
189 +       }
190 +
191 +
192         pr_info("BCM2708FB: allocated DMA channel %d @ %p\n",
193                fb->dma_chan, fb->dma_chan_base);
194  
195 @@ -635,6 +687,8 @@ static int bcm2708_fb_probe(struct platform_device *dev)
196                 goto out;
197         }
198  
199 +free_dma_chan:
200 +       bcm_dma_chan_free(fb->dma_chan);
201  free_cb:
202         dma_free_writecombine(&dev->dev, SZ_64K, fb->cb_base, fb->cb_handle);
203  free_fb:
204 @@ -662,6 +716,8 @@ static int bcm2708_fb_remove(struct platform_device *dev)
205                           fb->dma);
206         bcm2708_fb_debugfs_deinit(fb);
207  
208 +       free_irq(fb->dma_irq, fb);
209 +
210         kfree(fb);
211  
212         return 0;
213 -- 
214 1.9.1
215