bcm27xx: update patches from RPi foundation
[oweals/openwrt.git] / target / linux / bcm27xx / patches-5.4 / 950-0040-fbdev-add-FBIOCOPYAREA-ioctl.patch
1 From 764b96cc27c293fb37a8b9031ddb25290974e3a2 Mon Sep 17 00:00:00 2001
2 From: Siarhei Siamashka <siarhei.siamashka@gmail.com>
3 Date: Mon, 17 Jun 2013 13:32:11 +0300
4 Subject: [PATCH] fbdev: add FBIOCOPYAREA ioctl
5
6 Based on the patch authored by Ali Gholami Rudi at
7     https://lkml.org/lkml/2009/7/13/153
8
9 Provide an ioctl for userspace applications, but only if this operation
10 is hardware accelerated (otherwide it does not make any sense).
11
12 Signed-off-by: Siarhei Siamashka <siarhei.siamashka@gmail.com>
13
14 bcm2708_fb: Add ioctl for reading gpu memory through dma
15
16 video: bcm2708_fb: Add compat_ioctl support.
17
18 When using a 64 bit kernel with 32 bit userspace we need
19 compat ioctl handling for FBIODMACOPY as one of the
20 parameters is a pointer.
21
22 Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
23 ---
24  drivers/video/fbdev/bcm2708_fb.c | 167 ++++++++++++++++++++++++++++++-
25  drivers/video/fbdev/core/fbmem.c |  35 +++++++
26  include/uapi/linux/fb.h          |  12 +++
27  3 files changed, 213 insertions(+), 1 deletion(-)
28
29 --- a/drivers/video/fbdev/bcm2708_fb.c
30 +++ b/drivers/video/fbdev/bcm2708_fb.c
31 @@ -32,8 +32,10 @@
32  #include <linux/printk.h>
33  #include <linux/console.h>
34  #include <linux/debugfs.h>
35 +#include <linux/uaccess.h>
36  #include <linux/io.h>
37  #include <linux/dma-mapping.h>
38 +#include <linux/cred.h>
39  #include <soc/bcm2835/raspberrypi-firmware.h>
40  #include <linux/mutex.h>
41  
42 @@ -613,7 +615,110 @@ static int bcm2708_fb_pan_display(struct
43         return result;
44  }
45  
46 -static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
47 +static void dma_memcpy(struct bcm2708_fb *fb, dma_addr_t dst, dma_addr_t src,
48 +                      int size)
49 +{
50 +       struct bcm2708_fb_dev *fbdev = fb->fbdev;
51 +       struct bcm2708_dma_cb *cb = fbdev->cb_base;
52 +       int burst_size = (fbdev->dma_chan == 0) ? 8 : 2;
53 +
54 +       cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
55 +                  BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
56 +                  BCM2708_DMA_D_INC;
57 +       cb->dst = dst;
58 +       cb->src = src;
59 +       cb->length = size;
60 +       cb->stride = 0;
61 +       cb->pad[0] = 0;
62 +       cb->pad[1] = 0;
63 +       cb->next = 0;
64 +
65 +       // Not sure what to do if this gets a signal whilst waiting
66 +       if (mutex_lock_interruptible(&fbdev->dma_mutex))
67 +               return;
68 +
69 +       if (size < dma_busy_wait_threshold) {
70 +               bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
71 +               bcm_dma_wait_idle(fbdev->dma_chan_base);
72 +       } else {
73 +               void __iomem *local_dma_chan = fbdev->dma_chan_base;
74 +
75 +               cb->info |= BCM2708_DMA_INT_EN;
76 +               bcm_dma_start(fbdev->dma_chan_base, fbdev->cb_handle);
77 +               while (bcm_dma_is_busy(local_dma_chan)) {
78 +                       wait_event_interruptible(fbdev->dma_waitq,
79 +                                                !bcm_dma_is_busy(local_dma_chan));
80 +               }
81 +               fbdev->dma_stats.dma_irqs++;
82 +       }
83 +       fbdev->dma_stats.dma_copies++;
84 +
85 +       mutex_unlock(&fbdev->dma_mutex);
86 +}
87 +
88 +/* address with no aliases */
89 +#define INTALIAS_NORMAL(x) ((x) & ~0xc0000000)
90 +/* cache coherent but non-allocating in L1 and L2 */
91 +#define INTALIAS_L1L2_NONALLOCATING(x) (((x) & ~0xc0000000) | 0x80000000)
92 +
93 +static long vc_mem_copy(struct bcm2708_fb *fb, struct fb_dmacopy *ioparam)
94 +{
95 +       size_t size = PAGE_SIZE;
96 +       u32 *buf = NULL;
97 +       dma_addr_t bus_addr;
98 +       long rc = 0;
99 +       size_t offset;
100 +
101 +       /* restrict this to root user */
102 +       if (!uid_eq(current_euid(), GLOBAL_ROOT_UID)) {
103 +               rc = -EFAULT;
104 +               goto out;
105 +       }
106 +
107 +       if (!fb->gpu.base || !fb->gpu.length) {
108 +               pr_err("[%s]: Unable to determine gpu memory (%x,%x)\n",
109 +                      __func__, fb->gpu.base, fb->gpu.length);
110 +               return -EFAULT;
111 +       }
112 +
113 +       if (INTALIAS_NORMAL(ioparam->src) < fb->gpu.base ||
114 +           INTALIAS_NORMAL(ioparam->src) >= fb->gpu.base + fb->gpu.length) {
115 +               pr_err("[%s]: Invalid memory access %x (%x-%x)", __func__,
116 +                      INTALIAS_NORMAL(ioparam->src), fb->gpu.base,
117 +                      fb->gpu.base + fb->gpu.length);
118 +               return -EFAULT;
119 +       }
120 +
121 +       buf = dma_alloc_coherent(fb->fb.device, PAGE_ALIGN(size), &bus_addr,
122 +                                GFP_ATOMIC);
123 +       if (!buf) {
124 +               pr_err("[%s]: failed to dma_alloc_coherent(%zd)\n", __func__,
125 +                      size);
126 +               rc = -ENOMEM;
127 +               goto out;
128 +       }
129 +
130 +       for (offset = 0; offset < ioparam->length; offset += size) {
131 +               size_t remaining = ioparam->length - offset;
132 +               size_t s = min(size, remaining);
133 +               u8 *p = (u8 *)((uintptr_t)ioparam->src + offset);
134 +               u8 *q = (u8 *)ioparam->dst + offset;
135 +
136 +               dma_memcpy(fb, bus_addr,
137 +                          INTALIAS_L1L2_NONALLOCATING((dma_addr_t)p), size);
138 +               if (copy_to_user(q, buf, s) != 0) {
139 +                       pr_err("[%s]: failed to copy-to-user\n", __func__);
140 +                       rc = -EFAULT;
141 +                       goto out;
142 +               }
143 +       }
144 +out:
145 +       if (buf)
146 +               dma_free_coherent(fb->fb.device, PAGE_ALIGN(size), buf,
147 +                                 bus_addr);
148 +       return rc;
149 +}
150 +
151  static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd,
152                          unsigned long arg)
153  {
154 @@ -629,6 +734,21 @@ static int bcm2708_ioctl(struct fb_info
155                                             RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC,
156                                             &dummy, sizeof(dummy));
157                 break;
158 +
159 +       case FBIODMACOPY:
160 +       {
161 +               struct fb_dmacopy ioparam;
162 +               /* Get the parameter data.
163 +                */
164 +               if (copy_from_user
165 +                   (&ioparam, (void *)arg, sizeof(ioparam))) {
166 +                       pr_err("[%s]: failed to copy-from-user\n", __func__);
167 +                       ret = -EFAULT;
168 +                       break;
169 +               }
170 +               ret = vc_mem_copy(fb, &ioparam);
171 +               break;
172 +       }
173         default:
174                 dev_dbg(info->device, "Unknown ioctl 0x%x\n", cmd);
175                 return -ENOTTY;
176 @@ -639,6 +759,48 @@ static int bcm2708_ioctl(struct fb_info
177  
178         return ret;
179  }
180 +
181 +#ifdef CONFIG_COMPAT
182 +struct fb_dmacopy32 {
183 +       compat_uptr_t dst;
184 +       __u32 src;
185 +       __u32 length;
186 +};
187 +
188 +#define FBIODMACOPY32          _IOW('z', 0x22, struct fb_dmacopy32)
189 +
190 +static int bcm2708_compat_ioctl(struct fb_info *info, unsigned int cmd,
191 +                               unsigned long arg)
192 +{
193 +       struct bcm2708_fb *fb = to_bcm2708(info);
194 +       int ret;
195 +
196 +       switch (cmd) {
197 +       case FBIODMACOPY32:
198 +       {
199 +               struct fb_dmacopy32 param32;
200 +               struct fb_dmacopy param;
201 +               /* Get the parameter data.
202 +                */
203 +               if (copy_from_user(&param32, (void *)arg, sizeof(param32))) {
204 +                       pr_err("[%s]: failed to copy-from-user\n", __func__);
205 +                       ret = -EFAULT;
206 +                       break;
207 +               }
208 +               param.dst = compat_ptr(param32.dst);
209 +               param.src = param32.src;
210 +               param.length = param32.length;
211 +               ret = vc_mem_copy(fb, &param);
212 +               break;
213 +       }
214 +       default:
215 +               ret = bcm2708_ioctl(info, cmd, arg);
216 +               break;
217 +       }
218 +       return ret;
219 +}
220 +#endif
221 +
222  static void bcm2708_fb_fillrect(struct fb_info *info,
223                                 const struct fb_fillrect *rect)
224  {
225 @@ -831,6 +993,9 @@ static struct fb_ops bcm2708_fb_ops = {
226         .fb_imageblit = bcm2708_fb_imageblit,
227         .fb_pan_display = bcm2708_fb_pan_display,
228         .fb_ioctl = bcm2708_ioctl,
229 +#ifdef CONFIG_COMPAT
230 +       .fb_compat_ioctl = bcm2708_compat_ioctl,
231 +#endif
232  };
233  
234  static int bcm2708_fb_register(struct bcm2708_fb *fb)
235 --- a/drivers/video/fbdev/core/fbmem.c
236 +++ b/drivers/video/fbdev/core/fbmem.c
237 @@ -1076,6 +1076,30 @@ fb_blank(struct fb_info *info, int blank
238  }
239  EXPORT_SYMBOL(fb_blank);
240  
241 +static int fb_copyarea_user(struct fb_info *info,
242 +                           struct fb_copyarea *copy)
243 +{
244 +       int ret = 0;
245 +       lock_fb_info(info);
246 +       if (copy->dx >= info->var.xres ||
247 +           copy->sx >= info->var.xres ||
248 +           copy->width > info->var.xres ||
249 +           copy->dy >= info->var.yres ||
250 +           copy->sy >= info->var.yres ||
251 +           copy->height > info->var.yres ||
252 +           copy->dx + copy->width > info->var.xres ||
253 +           copy->sx + copy->width > info->var.xres ||
254 +           copy->dy + copy->height > info->var.yres ||
255 +           copy->sy + copy->height > info->var.yres) {
256 +               ret = -EINVAL;
257 +               goto out;
258 +       }
259 +       info->fbops->fb_copyarea(info, copy);
260 +out:
261 +       unlock_fb_info(info);
262 +       return ret;
263 +}
264 +
265  static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
266                         unsigned long arg)
267  {
268 @@ -1084,6 +1108,7 @@ static long do_fb_ioctl(struct fb_info *
269         struct fb_fix_screeninfo fix;
270         struct fb_cmap cmap_from;
271         struct fb_cmap_user cmap;
272 +       struct fb_copyarea copy;
273         void __user *argp = (void __user *)arg;
274         long ret = 0;
275  
276 @@ -1159,6 +1184,15 @@ static long do_fb_ioctl(struct fb_info *
277                 unlock_fb_info(info);
278                 console_unlock();
279                 break;
280 +       case FBIOCOPYAREA:
281 +               if (info->flags & FBINFO_HWACCEL_COPYAREA) {
282 +                       /* only provide this ioctl if it is accelerated */
283 +                       if (copy_from_user(&copy, argp, sizeof(copy)))
284 +                               return -EFAULT;
285 +                       ret = fb_copyarea_user(info, &copy);
286 +                       break;
287 +               }
288 +               /* fall through */
289         default:
290                 lock_fb_info(info);
291                 fb = info->fbops;
292 @@ -1304,6 +1338,7 @@ static long fb_compat_ioctl(struct file
293         case FBIOPAN_DISPLAY:
294         case FBIOGET_CON2FBMAP:
295         case FBIOPUT_CON2FBMAP:
296 +       case FBIOCOPYAREA:
297                 arg = (unsigned long) compat_ptr(arg);
298                 /* fall through */
299         case FBIOBLANK:
300 --- a/include/uapi/linux/fb.h
301 +++ b/include/uapi/linux/fb.h
302 @@ -35,6 +35,12 @@
303  #define FBIOPUT_MODEINFO        0x4617
304  #define FBIOGET_DISPINFO        0x4618
305  #define FBIO_WAITFORVSYNC      _IOW('F', 0x20, __u32)
306 +/*
307 + * HACK: use 'z' in order not to clash with any other ioctl numbers which might
308 + * be concurrently added to the mainline kernel
309 + */
310 +#define FBIOCOPYAREA           _IOW('z', 0x21, struct fb_copyarea)
311 +#define FBIODMACOPY            _IOW('z', 0x22, struct fb_dmacopy)
312  
313  #define FB_TYPE_PACKED_PIXELS          0       /* Packed Pixels        */
314  #define FB_TYPE_PLANES                 1       /* Non interleaved planes */
315 @@ -347,6 +353,12 @@ struct fb_copyarea {
316         __u32 sy;
317  };
318  
319 +struct fb_dmacopy {
320 +       void *dst;
321 +       __u32 src;
322 +       __u32 length;
323 +};
324 +
325  struct fb_fillrect {
326         __u32 dx;       /* screen-relative */
327         __u32 dy;