mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-27 17:18:59 +00:00
7d7aa2fd92
This change makes the names of Broadcom targets consistent by using the common notation based on SoC/CPU ID (which is used internally anyway), bcmXXXX instead of brcmXXXX. This is even used for target TITLE in make menuconfig already, only the short target name used brcm so far. Despite, since subtargets range from bcm2708 to bcm2711, it seems appropriate to use bcm27xx instead of bcm2708 (again, as already done for BOARDNAME). This also renames the packages brcm2708-userland and brcm2708-gpu-fw. Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de> Acked-by: Álvaro Fernández Rojas <noltari@gmail.com>
266 lines
7.5 KiB
Diff
266 lines
7.5 KiB
Diff
From 5269119dadfc6874aec51fed4468c73f1b0187a2 Mon Sep 17 00:00:00 2001
|
|
From: Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
|
Date: Mon, 17 Jun 2013 13:32:11 +0300
|
|
Subject: [PATCH] fbdev: add FBIOCOPYAREA ioctl
|
|
|
|
Based on the patch authored by Ali Gholami Rudi at
|
|
https://lkml.org/lkml/2009/7/13/153
|
|
|
|
Provide an ioctl for userspace applications, but only if this operation
|
|
is hardware accelerated (otherwide it does not make any sense).
|
|
|
|
Signed-off-by: Siarhei Siamashka <siarhei.siamashka@gmail.com>
|
|
|
|
bcm2708_fb: Add ioctl for reading gpu memory through dma
|
|
---
|
|
drivers/video/fbdev/bcm2708_fb.c | 119 ++++++++++++++++++++++++++++++-
|
|
drivers/video/fbdev/core/fbmem.c | 36 ++++++++++
|
|
include/uapi/linux/fb.h | 12 ++++
|
|
3 files changed, 166 insertions(+), 1 deletion(-)
|
|
|
|
--- a/drivers/video/fbdev/bcm2708_fb.c
|
|
+++ b/drivers/video/fbdev/bcm2708_fb.c
|
|
@@ -31,8 +31,10 @@
|
|
#include <linux/console.h>
|
|
#include <linux/debugfs.h>
|
|
#include <asm/sizes.h>
|
|
+#include <linux/uaccess.h>
|
|
#include <linux/io.h>
|
|
#include <linux/dma-mapping.h>
|
|
+#include <linux/cred.h>
|
|
#include <soc/bcm2835/raspberrypi-firmware.h>
|
|
|
|
//#define BCM2708_FB_DEBUG
|
|
@@ -95,6 +97,7 @@ struct bcm2708_fb {
|
|
wait_queue_head_t dma_waitq;
|
|
struct bcm2708_fb_stats stats;
|
|
unsigned long fb_bus_address;
|
|
+ struct { u32 base, length; } gpu;
|
|
};
|
|
|
|
#define to_bcm2708(info) container_of(info, struct bcm2708_fb, fb)
|
|
@@ -439,7 +442,118 @@ static int bcm2708_fb_pan_display(struct
|
|
return result;
|
|
}
|
|
|
|
-static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
|
|
+static void dma_memcpy(struct bcm2708_fb *fb, dma_addr_t dst, dma_addr_t src,
|
|
+ int size)
|
|
+{
|
|
+ int burst_size = (fb->dma_chan == 0) ? 8 : 2;
|
|
+ struct bcm2708_dma_cb *cb = fb->cb_base;
|
|
+
|
|
+ cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
|
|
+ BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
|
|
+ BCM2708_DMA_D_INC;
|
|
+ cb->dst = dst;
|
|
+ cb->src = src;
|
|
+ cb->length = size;
|
|
+ cb->stride = 0;
|
|
+ cb->pad[0] = 0;
|
|
+ cb->pad[1] = 0;
|
|
+ cb->next = 0;
|
|
+
|
|
+ if (size < 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++;
|
|
+}
|
|
+
|
|
+/* address with no aliases */
|
|
+#define INTALIAS_NORMAL(x) ((x)&~0xc0000000)
|
|
+/* cache coherent but non-allocating in L1 and L2 */
|
|
+#define INTALIAS_L1L2_NONALLOCATING(x) (((x)&~0xc0000000)|0x80000000)
|
|
+
|
|
+static long vc_mem_copy(struct bcm2708_fb *fb, unsigned long arg)
|
|
+{
|
|
+ struct fb_dmacopy ioparam;
|
|
+ size_t size = PAGE_SIZE;
|
|
+ u32 *buf = NULL;
|
|
+ dma_addr_t bus_addr;
|
|
+ long rc = 0;
|
|
+ size_t offset;
|
|
+
|
|
+ /* restrict this to root user */
|
|
+ if (!uid_eq(current_euid(), GLOBAL_ROOT_UID)) {
|
|
+ rc = -EFAULT;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Get the parameter data.
|
|
+ */
|
|
+ if (copy_from_user
|
|
+ (&ioparam, (void *)arg, sizeof(ioparam)) != 0) {
|
|
+ pr_err("[%s]: failed to copy-from-user\n",
|
|
+ __func__);
|
|
+ rc = -EFAULT;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (fb->gpu.base == 0 || fb->gpu.length == 0) {
|
|
+ pr_err("[%s]: Unable to determine gpu memory (%x,%x)\n",
|
|
+ __func__, fb->gpu.base, fb->gpu.length);
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ if (INTALIAS_NORMAL(ioparam.src) < fb->gpu.base ||
|
|
+ INTALIAS_NORMAL(ioparam.src) >= fb->gpu.base + fb->gpu.length) {
|
|
+ pr_err("[%s]: Invalid memory access %x (%x-%x)", __func__,
|
|
+ INTALIAS_NORMAL(ioparam.src), fb->gpu.base,
|
|
+ fb->gpu.base + fb->gpu.length);
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ buf = dma_alloc_coherent(fb->fb.device, PAGE_ALIGN(size), &bus_addr,
|
|
+ GFP_ATOMIC);
|
|
+ if (!buf) {
|
|
+ pr_err("[%s]: failed to dma_alloc_coherent(%d)\n",
|
|
+ __func__, size);
|
|
+ rc = -ENOMEM;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ for (offset = 0; offset < ioparam.length; offset += size) {
|
|
+ size_t remaining = ioparam.length - offset;
|
|
+ size_t s = min(size, remaining);
|
|
+ unsigned char *p = (unsigned char *)ioparam.src + offset;
|
|
+ unsigned char *q = (unsigned char *)ioparam.dst + offset;
|
|
+
|
|
+ dma_memcpy(fb, bus_addr,
|
|
+ INTALIAS_L1L2_NONALLOCATING((dma_addr_t)p), size);
|
|
+ if (copy_to_user(q, buf, s) != 0) {
|
|
+ pr_err("[%s]: failed to copy-to-user\n",
|
|
+ __func__);
|
|
+ rc = -EFAULT;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+out:
|
|
+ if (buf)
|
|
+ dma_free_coherent(fb->fb.device, PAGE_ALIGN(size), buf,
|
|
+ bus_addr);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int bcm2708_ioctl(struct fb_info *info, unsigned int cmd,
|
|
+ unsigned long arg)
|
|
{
|
|
struct bcm2708_fb *fb = to_bcm2708(info);
|
|
u32 dummy = 0;
|
|
@@ -451,6 +565,9 @@ static int bcm2708_ioctl(struct fb_info
|
|
RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC,
|
|
&dummy, sizeof(dummy));
|
|
break;
|
|
+ case FBIODMACOPY:
|
|
+ ret = vc_mem_copy(fb, arg);
|
|
+ break;
|
|
default:
|
|
dev_dbg(info->device, "Unknown ioctl 0x%x\n", cmd);
|
|
return -ENOTTY;
|
|
--- a/drivers/video/fbdev/core/fbmem.c
|
|
+++ b/drivers/video/fbdev/core/fbmem.c
|
|
@@ -1081,6 +1081,31 @@ fb_blank(struct fb_info *info, int blank
|
|
}
|
|
EXPORT_SYMBOL(fb_blank);
|
|
|
|
+static int fb_copyarea_user(struct fb_info *info,
|
|
+ struct fb_copyarea *copy)
|
|
+{
|
|
+ int ret = 0;
|
|
+ if (!lock_fb_info(info))
|
|
+ return -ENODEV;
|
|
+ if (copy->dx >= info->var.xres ||
|
|
+ copy->sx >= info->var.xres ||
|
|
+ copy->width > info->var.xres ||
|
|
+ copy->dy >= info->var.yres ||
|
|
+ copy->sy >= info->var.yres ||
|
|
+ copy->height > info->var.yres ||
|
|
+ copy->dx + copy->width > info->var.xres ||
|
|
+ copy->sx + copy->width > info->var.xres ||
|
|
+ copy->dy + copy->height > info->var.yres ||
|
|
+ copy->sy + copy->height > info->var.yres) {
|
|
+ ret = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+ info->fbops->fb_copyarea(info, copy);
|
|
+out:
|
|
+ unlock_fb_info(info);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
@@ -1091,6 +1116,7 @@ static long do_fb_ioctl(struct fb_info *
|
|
struct fb_cmap cmap_from;
|
|
struct fb_cmap_user cmap;
|
|
struct fb_event event;
|
|
+ struct fb_copyarea copy;
|
|
void __user *argp = (void __user *)arg;
|
|
long ret = 0;
|
|
|
|
@@ -1208,6 +1234,15 @@ static long do_fb_ioctl(struct fb_info *
|
|
unlock_fb_info(info);
|
|
console_unlock();
|
|
break;
|
|
+ case FBIOCOPYAREA:
|
|
+ if (info->flags & FBINFO_HWACCEL_COPYAREA) {
|
|
+ /* only provide this ioctl if it is accelerated */
|
|
+ if (copy_from_user(©, argp, sizeof(copy)))
|
|
+ return -EFAULT;
|
|
+ ret = fb_copyarea_user(info, ©);
|
|
+ break;
|
|
+ }
|
|
+ /* fall through */
|
|
default:
|
|
if (!lock_fb_info(info))
|
|
return -ENODEV;
|
|
@@ -1353,6 +1388,7 @@ static long fb_compat_ioctl(struct file
|
|
case FBIOPAN_DISPLAY:
|
|
case FBIOGET_CON2FBMAP:
|
|
case FBIOPUT_CON2FBMAP:
|
|
+ case FBIOCOPYAREA:
|
|
arg = (unsigned long) compat_ptr(arg);
|
|
/* fall through */
|
|
case FBIOBLANK:
|
|
--- a/include/uapi/linux/fb.h
|
|
+++ b/include/uapi/linux/fb.h
|
|
@@ -35,6 +35,12 @@
|
|
#define FBIOPUT_MODEINFO 0x4617
|
|
#define FBIOGET_DISPINFO 0x4618
|
|
#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
|
|
+/*
|
|
+ * HACK: use 'z' in order not to clash with any other ioctl numbers which might
|
|
+ * be concurrently added to the mainline kernel
|
|
+ */
|
|
+#define FBIOCOPYAREA _IOW('z', 0x21, struct fb_copyarea)
|
|
+#define FBIODMACOPY _IOW('z', 0x22, struct fb_dmacopy)
|
|
|
|
#define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */
|
|
#define FB_TYPE_PLANES 1 /* Non interleaved planes */
|
|
@@ -347,6 +353,12 @@ struct fb_copyarea {
|
|
__u32 sy;
|
|
};
|
|
|
|
+struct fb_dmacopy {
|
|
+ void *dst;
|
|
+ __u32 src;
|
|
+ __u32 length;
|
|
+};
|
|
+
|
|
struct fb_fillrect {
|
|
__u32 dx; /* screen-relative */
|
|
__u32 dy;
|