OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | From d56622a9c76ab913da66bb22c3158b10f9fb0543 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 056/454] 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 | drivers/video/fbdev/bcm2708_fb.c | 111 +++++++++++++++++++++++++++++++ |
||
17 | drivers/video/fbdev/core/fbmem.c | 36 ++++++++++ |
||
18 | include/uapi/linux/fb.h | 12 ++++ |
||
19 | 3 files changed, 159 insertions(+) |
||
20 | |||
21 | --- a/drivers/video/fbdev/bcm2708_fb.c |
||
22 | +++ b/drivers/video/fbdev/bcm2708_fb.c |
||
23 | @@ -31,8 +31,10 @@ |
||
24 | #include <linux/console.h> |
||
25 | #include <linux/debugfs.h> |
||
26 | #include <asm/sizes.h> |
||
27 | +#include <linux/uaccess.h> |
||
28 | #include <linux/io.h> |
||
29 | #include <linux/dma-mapping.h> |
||
30 | +#include <linux/cred.h> |
||
31 | #include <soc/bcm2835/raspberrypi-firmware.h> |
||
32 | |||
33 | //#define BCM2708_FB_DEBUG |
||
34 | @@ -94,6 +96,7 @@ struct bcm2708_fb { |
||
35 | wait_queue_head_t dma_waitq; |
||
36 | struct bcm2708_fb_stats stats; |
||
37 | unsigned long fb_bus_address; |
||
38 | + struct { u32 base, length; } gpu; |
||
39 | }; |
||
40 | |||
41 | #define to_bcm2708(info) container_of(info, struct bcm2708_fb, fb) |
||
42 | @@ -426,6 +429,106 @@ static int bcm2708_fb_pan_display(struct |
||
43 | return result; |
||
44 | } |
||
45 | |||
46 | +static void dma_memcpy(struct bcm2708_fb *fb, dma_addr_t dst, dma_addr_t src, int size) |
||
47 | +{ |
||
48 | + int burst_size = (fb->dma_chan == 0) ? 8 : 2; |
||
49 | + struct bcm2708_dma_cb *cb = fb->cb_base; |
||
50 | + |
||
51 | + cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH | |
||
52 | + BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH | |
||
53 | + BCM2708_DMA_D_INC; |
||
54 | + cb->dst = dst; |
||
55 | + cb->src = src; |
||
56 | + cb->length = size; |
||
57 | + cb->stride = 0; |
||
58 | + cb->pad[0] = 0; |
||
59 | + cb->pad[1] = 0; |
||
60 | + cb->next = 0; |
||
61 | + |
||
62 | + if (size < dma_busy_wait_threshold) { |
||
63 | + bcm_dma_start(fb->dma_chan_base, fb->cb_handle); |
||
64 | + bcm_dma_wait_idle(fb->dma_chan_base); |
||
65 | + } else { |
||
66 | + void __iomem *dma_chan = fb->dma_chan_base; |
||
67 | + cb->info |= BCM2708_DMA_INT_EN; |
||
68 | + bcm_dma_start(fb->dma_chan_base, fb->cb_handle); |
||
69 | + while (bcm_dma_is_busy(dma_chan)) { |
||
70 | + wait_event_interruptible( |
||
71 | + fb->dma_waitq, |
||
72 | + !bcm_dma_is_busy(dma_chan)); |
||
73 | + } |
||
74 | + fb->stats.dma_irqs++; |
||
75 | + } |
||
76 | + fb->stats.dma_copies++; |
||
77 | +} |
||
78 | + |
||
79 | +#define INTALIAS_NORMAL(x) ((x)&~0xc0000000) // address with no aliases |
||
80 | +#define INTALIAS_L1L2_NONALLOCATING(x) (((x)&~0xc0000000)|0x80000000) // cache coherent but non-allocating in L1 and L2 |
||
81 | + |
||
82 | +static long vc_mem_copy(struct bcm2708_fb *fb, unsigned long arg) |
||
83 | +{ |
||
84 | + struct fb_dmacopy ioparam; |
||
85 | + size_t size = PAGE_SIZE; |
||
86 | + u32 *buf = NULL; |
||
87 | + dma_addr_t bus_addr; |
||
88 | + long rc = 0; |
||
89 | + size_t offset; |
||
90 | + |
||
91 | + /* restrict this to root user */ |
||
92 | + if (!uid_eq(current_euid(), GLOBAL_ROOT_UID)) |
||
93 | + { |
||
94 | + rc = -EFAULT; |
||
95 | + goto out; |
||
96 | + } |
||
97 | + |
||
98 | + /* Get the parameter data. |
||
99 | + */ |
||
100 | + if (copy_from_user |
||
101 | + (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { |
||
102 | + pr_err("[%s]: failed to copy-from-user\n", |
||
103 | + __func__); |
||
104 | + rc = -EFAULT; |
||
105 | + goto out; |
||
106 | + } |
||
107 | + |
||
108 | + if (fb->gpu.base == 0 || fb->gpu.length == 0) { |
||
109 | + pr_err("[%s]: Unable to determine gpu memory (%x,%x)\n", __func__, fb->gpu.base, fb->gpu.length); |
||
110 | + return -EFAULT; |
||
111 | + } |
||
112 | + |
||
113 | + if (INTALIAS_NORMAL(ioparam.src) < fb->gpu.base || INTALIAS_NORMAL(ioparam.src) >= fb->gpu.base + fb->gpu.length) { |
||
114 | + pr_err("[%s]: Invalid memory access %x (%x-%x)", __func__, INTALIAS_NORMAL(ioparam.src), fb->gpu.base, fb->gpu.base + fb->gpu.length); |
||
115 | + return -EFAULT; |
||
116 | + } |
||
117 | + |
||
118 | + buf = dma_alloc_coherent(fb->fb.device, PAGE_ALIGN(size), &bus_addr, |
||
119 | + GFP_ATOMIC); |
||
120 | + if (!buf) { |
||
121 | + pr_err("[%s]: failed to dma_alloc_coherent(%d)\n", |
||
122 | + __func__, size); |
||
123 | + rc = -ENOMEM; |
||
124 | + goto out; |
||
125 | + } |
||
126 | + |
||
127 | + for (offset = 0; offset < ioparam.length; offset += size) { |
||
128 | + size_t remaining = ioparam.length - offset; |
||
129 | + size_t s = min(size, remaining); |
||
130 | + unsigned char *p = (unsigned char *)ioparam.src + offset; |
||
131 | + unsigned char *q = (unsigned char *)ioparam.dst + offset; |
||
132 | + dma_memcpy(fb, bus_addr, INTALIAS_L1L2_NONALLOCATING((dma_addr_t)p), size); |
||
133 | + if (copy_to_user(q, buf, s) != 0) { |
||
134 | + pr_err("[%s]: failed to copy-to-user\n", |
||
135 | + __func__); |
||
136 | + rc = -EFAULT; |
||
137 | + goto out; |
||
138 | + } |
||
139 | + } |
||
140 | +out: |
||
141 | + if (buf) |
||
142 | + dma_free_coherent(fb->fb.device, PAGE_ALIGN(size), buf, bus_addr); |
||
143 | + return rc; |
||
144 | +} |
||
145 | + |
||
146 | static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) |
||
147 | { |
||
148 | struct bcm2708_fb *fb = to_bcm2708(info); |
||
149 | @@ -438,6 +541,9 @@ static int bcm2708_ioctl(struct fb_info |
||
150 | RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC, |
||
151 | &dummy, sizeof(dummy)); |
||
152 | break; |
||
153 | + case FBIODMACOPY: |
||
154 | + ret = vc_mem_copy(fb, arg); |
||
155 | + break; |
||
156 | default: |
||
157 | dev_dbg(info->device, "Unknown ioctl 0x%x\n", cmd); |
||
158 | return -ENOTTY; |
||
159 | @@ -760,6 +866,11 @@ static int bcm2708_fb_probe(struct platf |
||
160 | fb->dev = dev; |
||
161 | fb->fb.device = &dev->dev; |
||
162 | |||
163 | + // failure here isn't fatal, but we'll fail in vc_mem_copy if fb->gpu is not valid |
||
164 | + rpi_firmware_property(fb->fw, |
||
165 | + RPI_FIRMWARE_GET_VC_MEMORY, |
||
166 | + &fb->gpu, sizeof(fb->gpu)); |
||
167 | + |
||
168 | ret = bcm2708_fb_register(fb); |
||
169 | if (ret == 0) { |
||
170 | platform_set_drvdata(dev, fb); |
||
171 | --- a/drivers/video/fbdev/core/fbmem.c |
||
172 | +++ b/drivers/video/fbdev/core/fbmem.c |
||
173 | @@ -1090,6 +1090,31 @@ fb_blank(struct fb_info *info, int blank |
||
174 | } |
||
175 | EXPORT_SYMBOL(fb_blank); |
||
176 | |||
177 | +static int fb_copyarea_user(struct fb_info *info, |
||
178 | + struct fb_copyarea *copy) |
||
179 | +{ |
||
180 | + int ret = 0; |
||
181 | + if (!lock_fb_info(info)) |
||
182 | + return -ENODEV; |
||
183 | + if (copy->dx >= info->var.xres || |
||
184 | + copy->sx >= info->var.xres || |
||
185 | + copy->width > info->var.xres || |
||
186 | + copy->dy >= info->var.yres || |
||
187 | + copy->sy >= info->var.yres || |
||
188 | + copy->height > info->var.yres || |
||
189 | + copy->dx + copy->width > info->var.xres || |
||
190 | + copy->sx + copy->width > info->var.xres || |
||
191 | + copy->dy + copy->height > info->var.yres || |
||
192 | + copy->sy + copy->height > info->var.yres) { |
||
193 | + ret = -EINVAL; |
||
194 | + goto out; |
||
195 | + } |
||
196 | + info->fbops->fb_copyarea(info, copy); |
||
197 | +out: |
||
198 | + unlock_fb_info(info); |
||
199 | + return ret; |
||
200 | +} |
||
201 | + |
||
202 | static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, |
||
203 | unsigned long arg) |
||
204 | { |
||
205 | @@ -1100,6 +1125,7 @@ static long do_fb_ioctl(struct fb_info * |
||
206 | struct fb_cmap cmap_from; |
||
207 | struct fb_cmap_user cmap; |
||
208 | struct fb_event event; |
||
209 | + struct fb_copyarea copy; |
||
210 | void __user *argp = (void __user *)arg; |
||
211 | long ret = 0; |
||
212 | |||
213 | @@ -1217,6 +1243,15 @@ static long do_fb_ioctl(struct fb_info * |
||
214 | unlock_fb_info(info); |
||
215 | console_unlock(); |
||
216 | break; |
||
217 | + case FBIOCOPYAREA: |
||
218 | + if (info->flags & FBINFO_HWACCEL_COPYAREA) { |
||
219 | + /* only provide this ioctl if it is accelerated */ |
||
220 | + if (copy_from_user(©, argp, sizeof(copy))) |
||
221 | + return -EFAULT; |
||
222 | + ret = fb_copyarea_user(info, ©); |
||
223 | + break; |
||
224 | + } |
||
225 | + /* fall through */ |
||
226 | default: |
||
227 | if (!lock_fb_info(info)) |
||
228 | return -ENODEV; |
||
229 | @@ -1362,6 +1397,7 @@ static long fb_compat_ioctl(struct file |
||
230 | case FBIOPAN_DISPLAY: |
||
231 | case FBIOGET_CON2FBMAP: |
||
232 | case FBIOPUT_CON2FBMAP: |
||
233 | + case FBIOCOPYAREA: |
||
234 | arg = (unsigned long) compat_ptr(arg); |
||
235 | case FBIOBLANK: |
||
236 | ret = do_fb_ioctl(info, cmd, arg); |
||
237 | --- a/include/uapi/linux/fb.h |
||
238 | +++ b/include/uapi/linux/fb.h |
||
239 | @@ -35,6 +35,12 @@ |
||
240 | #define FBIOPUT_MODEINFO 0x4617 |
||
241 | #define FBIOGET_DISPINFO 0x4618 |
||
242 | #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) |
||
243 | +/* |
||
244 | + * HACK: use 'z' in order not to clash with any other ioctl numbers which might |
||
245 | + * be concurrently added to the mainline kernel |
||
246 | + */ |
||
247 | +#define FBIOCOPYAREA _IOW('z', 0x21, struct fb_copyarea) |
||
248 | +#define FBIODMACOPY _IOW('z', 0x22, struct fb_dmacopy) |
||
249 | |||
250 | #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ |
||
251 | #define FB_TYPE_PLANES 1 /* Non interleaved planes */ |
||
252 | @@ -347,6 +353,12 @@ struct fb_copyarea { |
||
253 | __u32 sy; |
||
254 | }; |
||
255 | |||
256 | +struct fb_dmacopy { |
||
257 | + void *dst; |
||
258 | + __u32 src; |
||
259 | + __u32 length; |
||
260 | +}; |
||
261 | + |
||
262 | struct fb_fillrect { |
||
263 | __u32 dx; /* screen-relative */ |
||
264 | __u32 dy; |