* [PATCH 09/11] drm: add SimpleDRM driver
From: David Herrmann @ 2014-01-23 14:15 UTC (permalink / raw)
To: dri-devel
Cc: Ingo Molnar, linux-fbdev, Dave Airlie, Daniel Vetter,
Tomi Valkeinen, linux-kernel, Tom Gundersen, David Herrmann
In-Reply-To: <1390486503-1504-1-git-send-email-dh.herrmann@gmail.com>
The SimpleDRM driver binds to simple-framebuffer devices and provides a
DRM/KMS API. It provides only a single CRTC+encoder+connector combination
plus one initial mode.
Userspace can create dumb-buffers which can be blit into the real
framebuffer similar to UDL. No access to the real framebuffer is allowed
(compared to earlier version of this driver) to avoid security issues.
Furthermore, this way we can support arbitrary modes as long as we have a
convertion-helper.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
MAINTAINERS | 8 +
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/simpledrm/Kconfig | 18 ++
drivers/gpu/drm/simpledrm/Makefile | 3 +
drivers/gpu/drm/simpledrm/simpledrm.c | 252 ++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm.h | 100 ++++++++
drivers/gpu/drm/simpledrm/simpledrm_damage.c | 306 ++++++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm_gem.c | 282 +++++++++++++++++++++
drivers/gpu/drm/simpledrm/simpledrm_kms.c | 365 +++++++++++++++++++++++++++
10 files changed, 1337 insertions(+)
create mode 100644 drivers/gpu/drm/simpledrm/Kconfig
create mode 100644 drivers/gpu/drm/simpledrm/Makefile
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.c
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.h
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_damage.c
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 31a0462..e7adc84 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7725,6 +7725,14 @@ S: Odd Fixes
F: drivers/media/platform/sh_vou.c
F: include/media/sh_vou.h
+SIMPLE DRM DRIVER
+M: David Herrmann <dh.herrmann@gmail.com>
+L: dri-devel@lists.freedesktop.org
+T: git git://people.freedesktop.org/~dvdhrm/linux
+S: Maintained
+F: drivers/gpu/drm/simpledrm
+F: include/linux/platform_data/simpledrm.h
+
SIMPLE FIRMWARE INTERFACE (SFI)
M: Len Brown <lenb@kernel.org>
L: sfi-devel@simplefirmware.org
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 8e7fa4d..f795359 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -199,3 +199,5 @@ source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"
source "drivers/gpu/drm/panel/Kconfig"
+
+source "drivers/gpu/drm/simpledrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 292a79d..7b27e07 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -61,5 +61,6 @@ obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_BOCHS) += bochs/
obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
+obj-$(CONFIG_DRM_SIMPLEDRM) += simpledrm/
obj-y += i2c/
obj-y += panel/
diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig
new file mode 100644
index 0000000..35bcce8
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/Kconfig
@@ -0,0 +1,18 @@
+config DRM_SIMPLEDRM
+ tristate "Simple firmware framebuffer DRM driver"
+ depends on DRM && (FB_SIMPLE = n)
+ help
+ SimpleDRM can run on all systems with pre-initialized graphics
+ hardware. It uses a framebuffer that was initialized during
+ firmware boot. No page-flipping, modesetting or other advanced
+ features are available. However, other DRM drivers can be loaded
+ later and take over from SimpleDRM if they provide real hardware
+ support.
+
+ SimpleDRM supports "simple-framebuffer" DeviceTree objects and
+ compatible platform framebuffers.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called simpledrm.
diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile
new file mode 100644
index 0000000..35f73ea
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -0,0 +1,3 @@
+sdrm-y := simpledrm.o simpledrm_kms.o simpledrm_gem.o simpledrm_damage.o
+
+obj-$(CONFIG_DRM_SIMPLEDRM) := sdrm.o
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.c b/drivers/gpu/drm/simpledrm/simpledrm.c
new file mode 100644
index 0000000..98273f5
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm.c
@@ -0,0 +1,252 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include "simpledrm.h"
+
+static const struct file_operations sdrm_drm_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = sdrm_drm_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+ .release = drm_release,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .llseek = noop_llseek,
+};
+
+static struct drm_driver sdrm_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM,
+ .load = sdrm_drm_load,
+ .unload = sdrm_drm_unload,
+ .fops = &sdrm_drm_fops,
+
+ .gem_free_object = sdrm_gem_free_object,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = sdrm_gem_prime_import,
+
+ .dumb_create = sdrm_dumb_create,
+ .dumb_map_offset = sdrm_dumb_map_offset,
+ .dumb_destroy = sdrm_dumb_destroy,
+
+ .name = "simpledrm",
+ .desc = "Simple firmware framebuffer DRM driver",
+ .date = "20130601",
+ .major = 0,
+ .minor = 0,
+ .patchlevel = 1,
+};
+
+static int parse_dt(struct platform_device *pdev,
+ struct simplefb_platform_data *mode)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const char *format;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ ret = of_property_read_u32(np, "width", &mode->width);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't parse width property\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "height", &mode->height);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't parse height property\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "stride", &mode->stride);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't parse stride property\n");
+ return ret;
+ }
+
+ ret = of_property_read_string(np, "format", &format);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't parse format property\n");
+ return ret;
+ }
+ mode->format = format;
+
+ return 0;
+}
+
+static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS;
+
+int sdrm_pdev_init(struct sdrm_device *sdrm)
+{
+ struct platform_device *pdev = sdrm->ddev->platformdev;
+ struct simplefb_platform_data *mode = pdev->dev.platform_data;
+ struct simplefb_platform_data pmode;
+ struct resource *mem;
+ unsigned int depth;
+ int ret, i, bpp;
+
+ if (!mode) {
+ mode = &pmode;
+ ret = parse_dt(pdev, mode);
+ if (ret)
+ return ret;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(sdrm->ddev->dev, "No memory resource\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(simplefb_formats); ++i) {
+ if (strcmp(mode->format, simplefb_formats[i].name))
+ continue;
+
+ sdrm->fb_sformat = &simplefb_formats[i];
+ sdrm->fb_format = simplefb_formats[i].fourcc;
+ sdrm->fb_width = mode->width;
+ sdrm->fb_height = mode->height;
+ sdrm->fb_stride = mode->stride;
+ sdrm->fb_base = mem->start;
+ sdrm->fb_size = resource_size(mem);
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(simplefb_formats)) {
+ dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format);
+ return -ENODEV;
+ }
+
+ switch (sdrm->fb_format) {
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_ARGB2101010:
+ /* You must adjust sdrm_put() whenever you add a new format
+ * here, otherwise, blitting operations will not work.
+ * Furthermore, include/linux/platform_data/simplefb.h needs
+ * to be adjusted so the platform-device actually allows this
+ * format. */
+ break;
+ default:
+ dev_err(sdrm->ddev->dev, "Unsupported format %s\n",
+ mode->format);
+ return -ENODEV;
+ }
+
+ drm_fb_get_bpp_depth(sdrm->fb_format, &depth, &bpp);
+ if (!bpp) {
+ dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format);
+ return -ENODEV;
+ }
+
+ if (sdrm->fb_size < sdrm->fb_stride * sdrm->fb_height) {
+ dev_err(sdrm->ddev->dev, "FB too small\n");
+ return -ENODEV;
+ } else if ((bpp + 7) / 8 * sdrm->fb_width > sdrm->fb_stride) {
+ dev_err(sdrm->ddev->dev, "Invalid stride\n");
+ return -ENODEV;
+ }
+
+ sdrm->fb_bpp = bpp;
+
+ sdrm->fb_map = ioremap_wc(sdrm->fb_base, sdrm->fb_size);
+ if (!sdrm->fb_map) {
+ dev_err(sdrm->ddev->dev, "cannot remap VMEM\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void sdrm_pdev_destroy(struct sdrm_device *sdrm)
+{
+ if (sdrm->fb_map) {
+ iounmap(sdrm->fb_map);
+ sdrm->fb_map = NULL;
+ }
+}
+
+static int sdrm_simplefb_probe(struct platform_device *pdev)
+{
+ return drm_platform_init(&sdrm_drm_driver, pdev);
+}
+
+static int sdrm_simplefb_remove(struct platform_device *pdev)
+{
+ struct drm_device *ddev = platform_get_drvdata(pdev);
+ struct sdrm_device *sdrm = ddev->dev_private;
+
+ /* Oh, god! This is racy, but always has been. Unless we finally get
+ * immediate device removal in DRM, we can never guarantee that no other
+ * CPU is currently doing some GEM-bo access in parallel to us. But, eh,
+ * what can we do.. Fixes for that is being worked on. */
+
+ drm_connector_unplug_all(ddev);
+
+ /* protect fb_map removal against sdrm_blit() */
+ drm_modeset_lock_all(ddev);
+ sdrm_pdev_destroy(sdrm);
+ drm_modeset_unlock_all(ddev);
+
+ drm_unplug_dev(ddev);
+
+ return 0;
+}
+
+static const struct of_device_id simplefb_of_match[] = {
+ { .compatible = "simple-framebuffer", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, simplefb_of_match);
+
+static struct platform_driver sdrm_simplefb_driver = {
+ .probe = sdrm_simplefb_probe,
+ .remove = sdrm_simplefb_remove,
+ .driver = {
+ .name = "simple-framebuffer",
+ .mod_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .of_match_table = simplefb_of_match,
+ },
+};
+
+static int __init sdrm_init(void)
+{
+ return platform_driver_register(&sdrm_simplefb_driver);
+}
+
+static void __exit sdrm_exit(void)
+{
+ platform_driver_unregister(&sdrm_simplefb_driver);
+}
+
+module_init(sdrm_init);
+module_exit(sdrm_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
+MODULE_DESCRIPTION("Simple firmware framebuffer DRM driver");
+MODULE_ALIAS("platform:simple-framebuffer");
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h
new file mode 100644
index 0000000..4ece7f5
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -0,0 +1,100 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#ifndef SDRM_DRV_H
+#define SDRM_DRV_H
+
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+
+struct sdrm_device;
+struct sdrm_gem_object;
+struct sdrm_framebuffer;
+
+/* simpledrm devices */
+
+struct sdrm_device {
+ struct drm_device *ddev;
+
+ /* framebuffer information */
+ const struct simplefb_format *fb_sformat;
+ u32 fb_format;
+ u32 fb_width;
+ u32 fb_height;
+ u32 fb_stride;
+ u32 fb_bpp;
+ unsigned long fb_base;
+ unsigned long fb_size;
+ void *fb_map;
+
+ /* mode-setting objects */
+ struct drm_crtc crtc;
+ struct drm_encoder enc;
+ struct drm_connector conn;
+ struct drm_display_mode *mode;
+};
+
+int sdrm_drm_load(struct drm_device *ddev, unsigned long flags);
+int sdrm_drm_unload(struct drm_device *ddev);
+int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma);
+int sdrm_pdev_init(struct sdrm_device *sdrm);
+void sdrm_pdev_destroy(struct sdrm_device *sdrm);
+
+int sdrm_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips);
+int sdrm_dirty_all_locked(struct sdrm_device *sdrm);
+int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm);
+
+/* simpledrm gem objects */
+
+struct sdrm_gem_object {
+ struct drm_gem_object base;
+ struct sg_table *sg;
+ struct page **pages;
+ void *vmapping;
+};
+
+#define to_sdrm_bo(x) container_of(x, struct sdrm_gem_object, base)
+
+struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
+ size_t size);
+struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
+ struct dma_buf *dma_buf);
+void sdrm_gem_free_object(struct drm_gem_object *obj);
+int sdrm_gem_get_pages(struct sdrm_gem_object *obj);
+
+/* dumb buffers */
+
+int sdrm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev,
+ struct drm_mode_create_dumb *arg);
+int sdrm_dumb_destroy(struct drm_file *file_priv, struct drm_device *ddev,
+ uint32_t handle);
+int sdrm_dumb_map_offset(struct drm_file *file_priv, struct drm_device *ddev,
+ uint32_t handle, uint64_t *offset);
+
+/* simpledrm framebuffers */
+
+struct sdrm_framebuffer {
+ struct drm_framebuffer base;
+ struct sdrm_gem_object *obj;
+};
+
+#define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base)
+
+#endif /* SDRM_DRV_H */
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_damage.c b/drivers/gpu/drm/simpledrm/simpledrm_damage.c
new file mode 100644
index 0000000..67a2841
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c
@@ -0,0 +1,306 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/dma-buf.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include "simpledrm.h"
+
+static inline void sdrm_put(u8 *dst, u32 four_cc, u16 r, u16 g, u16 b)
+{
+ switch (four_cc) {
+ case DRM_FORMAT_RGB565:
+ r &= 0x1f;
+ g &= 0x3f;
+ b &= 0x1f;
+ put_unaligned((u16)((r << 11) | (g << 5) | b), (u16*)dst);
+ break;
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_ARGB1555:
+ r &= 0x1f;
+ g &= 0x1f;
+ b &= 0x1f;
+ put_unaligned((u16)((r << 10) | (g << 5) | b), (u16*)dst);
+ break;
+ case DRM_FORMAT_RGB888:
+ r &= 0xff;
+ g &= 0xff;
+ b &= 0xff;
+#ifdef __LITTLE_ENDIAN
+ dst[2] = r;
+ dst[1] = g;
+ dst[0] = b;
+#elif defined(__BIG_ENDIAN)
+ dst[0] = r;
+ dst[1] = g;
+ dst[2] = b;
+#endif
+ break;
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB8888:
+ r &= 0xff;
+ g &= 0xff;
+ b &= 0xff;
+ put_unaligned((u32)((r << 16) | (g << 8) | b), (u32*)dst);
+ break;
+ case DRM_FORMAT_ABGR8888:
+ r &= 0xff;
+ g &= 0xff;
+ b &= 0xff;
+ put_unaligned((u32)((b << 16) | (g << 8) | r), (u32*)dst);
+ break;
+ case DRM_FORMAT_XRGB2101010:
+ case DRM_FORMAT_ARGB2101010:
+ r &= 0x3ff;
+ g &= 0x3ff;
+ b &= 0x3ff;
+ put_unaligned((u32)((r << 20) | (g << 10) | b), (u32*)dst);
+ break;
+ }
+}
+
+static void sdrm_blit_from_xrgb8888(const u8 *src, u32 src_stride, u32 src_Bpp,
+ u8 *dst, u32 dst_stride, u32 dst_Bpp,
+ u32 dst_four_cc, u32 width, u32 height)
+{
+ u32 val, i;
+
+ while (height--) {
+ for (i = 0; i < width; ++i) {
+ val = get_unaligned((const u32*)&src[i * src_Bpp]);
+ sdrm_put(&dst[i * dst_Bpp], dst_four_cc,
+ (val & 0x00ff0000U) >> 16,
+ (val & 0x0000ff00U) >> 8,
+ (val & 0x000000ffU));
+ }
+
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void sdrm_blit_from_rgb565(const u8 *src, u32 src_stride, u32 src_Bpp,
+ u8 *dst, u32 dst_stride, u32 dst_Bpp,
+ u32 dst_four_cc, u32 width, u32 height)
+{
+ u32 val, i;
+
+ while (height--) {
+ for (i = 0; i < width; ++i) {
+ val = get_unaligned((const u16*)&src[i * src_Bpp]);
+ sdrm_put(&dst[i * dst_Bpp], dst_four_cc,
+ (val & 0xf800) >> 11,
+ (val & 0x07e0) >> 5,
+ (val & 0x001f));
+ }
+
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void sdrm_blit_lines(const u8 *src, u32 src_stride,
+ u8 *dst, u32 dst_stride,
+ u32 Bpp, u32 width, u32 height)
+{
+ u32 len;
+
+ len = width * Bpp;
+
+ while (height--) {
+ memcpy(dst, src, len);
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void sdrm_blit(struct sdrm_framebuffer *sfb, u32 x, u32 y,
+ u32 width, u32 height)
+{
+ struct drm_framebuffer *fb = &sfb->base;
+ struct drm_device *ddev = fb->dev;
+ struct sdrm_device *sdrm = ddev->dev_private;
+ u32 src_Bpp, dst_Bpp, xoff, yoff, x2, y2;
+ u8 *src, *dst;
+
+ /* already unmapped; ongoing handover? */
+ if (!sdrm->fb_map)
+ return;
+
+ /* empty dirty-region, nothing to do */
+ if (!width || !height)
+ return;
+ if (x >= fb->width || y >= fb->height)
+ return;
+
+ /* sanity checks */
+ if (x + width < x)
+ width = fb->width - x;
+ if (y + height < y)
+ height = fb->height - y;
+
+ /* get scanout offsets */
+ xoff = 0;
+ yoff = 0;
+ if (sdrm->crtc.fb = fb) {
+ xoff = sdrm->crtc.x;
+ yoff = sdrm->crtc.y;
+ }
+
+ /* get intersection of dirty region and scan-out region */
+ x2 = min(x + width, xoff + sdrm->fb_width);
+ y2 = min(y + height, yoff + sdrm->fb_height);
+ x = max(x, xoff);
+ y = max(y, yoff);
+ if (x2 <= x || y2 <= y)
+ return;
+ width = x2 - x;
+ height = y2 - y;
+
+ /* get buffer offsets */
+ src = sfb->obj->vmapping;
+ dst = sdrm->fb_map;
+
+ /* bo is guaranteed to be big enough; size checks not needed */
+ src_Bpp = (fb->bits_per_pixel + 7) / 8;
+ src += fb->offsets[0] + y * fb->pitches[0] + x * src_Bpp;
+
+ dst_Bpp = (sdrm->fb_bpp + 7) / 8;
+ dst += (y - yoff) * sdrm->fb_stride + (x - xoff) * dst_Bpp;
+
+ /* if formats are identical, do a line-by-line copy.. */
+ if (fb->pixel_format = sdrm->fb_format) {
+ sdrm_blit_lines(src, fb->pitches[0],
+ dst, sdrm->fb_stride,
+ src_Bpp, width, height);
+ return;
+ }
+
+ /* ..otherwise call slow blit-function */
+ switch (fb->pixel_format) {
+ case DRM_FORMAT_ARGB8888:
+ /* fallthrough */
+ case DRM_FORMAT_XRGB8888:
+ sdrm_blit_from_xrgb8888(src, fb->pitches[0], src_Bpp,
+ dst, sdrm->fb_stride, dst_Bpp,
+ sdrm->fb_format, width, height);
+ break;
+ case DRM_FORMAT_RGB565:
+ sdrm_blit_from_rgb565(src, fb->pitches[0], src_Bpp,
+ dst, sdrm->fb_stride, dst_Bpp,
+ sdrm->fb_format, width, height);
+ break;
+ }
+}
+
+static int sdrm_begin_access(struct sdrm_framebuffer *sfb)
+{
+ int r;
+
+ r = sdrm_gem_get_pages(sfb->obj);
+ if (r)
+ return r;
+
+ if (!sfb->obj->base.import_attach)
+ return 0;
+
+ return dma_buf_begin_cpu_access(sfb->obj->base.import_attach->dmabuf,
+ 0, sfb->obj->base.size,
+ DMA_FROM_DEVICE);
+}
+
+static void sdrm_end_access(struct sdrm_framebuffer *sfb)
+{
+ if (!sfb->obj->base.import_attach)
+ return;
+
+ dma_buf_end_cpu_access(sfb->obj->base.import_attach->dmabuf,
+ 0, sfb->obj->base.size,
+ DMA_FROM_DEVICE);
+}
+
+int sdrm_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
+ struct drm_device *ddev = fb->dev;
+ struct sdrm_device *sdrm = ddev->dev_private;
+ unsigned int i;
+ int r;
+
+ drm_modeset_lock_all(ddev);
+
+ if (sdrm->crtc.fb != fb) {
+ r = 0;
+ goto unlock;
+ }
+
+ r = sdrm_begin_access(sfb);
+ if (r)
+ goto unlock;
+
+ for (i = 0; i < num_clips; i++) {
+ if (clips[i].x2 <= clips[i].x1 ||
+ clips[i].y2 <= clips[i].y1)
+ continue;
+
+ sdrm_blit(sfb, clips[i].x1, clips[i].y1,
+ clips[i].x2 - clips[i].x1,
+ clips[i].y2 - clips[i].y1);
+ }
+
+ sdrm_end_access(sfb);
+
+unlock:
+ drm_modeset_unlock_all(ddev);
+ return 0;
+}
+
+int sdrm_dirty_all_locked(struct sdrm_device *sdrm)
+{
+ struct drm_framebuffer *fb;
+ struct sdrm_framebuffer *sfb;
+ int r;
+
+ fb = sdrm->crtc.fb;
+ if (!fb)
+ return 0;
+
+ sfb = to_sdrm_fb(fb);
+ r = sdrm_begin_access(sfb);
+ if (r)
+ return r;
+
+ sdrm_blit(sfb, 0, 0, fb->width, fb->height);
+
+ sdrm_end_access(sfb);
+
+ return 0;
+}
+
+int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm)
+{
+ int r;
+
+ drm_modeset_lock_all(sdrm->ddev);
+ r = sdrm_dirty_all_locked(sdrm);
+ drm_modeset_unlock_all(sdrm->ddev);
+
+ return r;
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_gem.c b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
new file mode 100644
index 0000000..f12c56c
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c
@@ -0,0 +1,282 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include "simpledrm.h"
+
+int sdrm_gem_get_pages(struct sdrm_gem_object *obj)
+{
+ size_t num, i;
+
+ if (obj->vmapping)
+ return 0;
+
+ if (obj->base.import_attach) {
+ obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf);
+ return !obj->vmapping ? -ENOMEM : 0;
+ }
+
+ num = obj->base.size >> PAGE_SHIFT;
+ obj->pages = drm_malloc_ab(num, sizeof(*obj->pages));
+ if (!obj->pages)
+ return -ENOMEM;
+
+ for (i = 0; i < num; ++i) {
+ obj->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!obj->pages[i])
+ goto error;
+ }
+
+ obj->vmapping = vmap(obj->pages, num, 0, PAGE_KERNEL);
+ if (!obj->vmapping)
+ goto error;
+
+ return 0;
+
+error:
+ while (i > 0)
+ __free_pages(obj->pages[--i], 0);
+
+ drm_free_large(obj->pages);
+ obj->pages = NULL;
+ return -ENOMEM;
+}
+
+static void sdrm_gem_put_pages(struct sdrm_gem_object *obj)
+{
+ size_t num, i;
+
+ if (!obj->vmapping)
+ return;
+
+ if (obj->base.import_attach) {
+ dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping);
+ obj->vmapping = NULL;
+ return;
+ }
+
+ vunmap(obj->vmapping);
+ obj->vmapping = NULL;
+
+ num = obj->base.size >> PAGE_SHIFT;
+ for (i = 0; i < num; ++i)
+ __free_pages(obj->pages[i], 0);
+
+ drm_free_large(obj->pages);
+ obj->pages = NULL;
+}
+
+struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev,
+ size_t size)
+{
+ struct sdrm_gem_object *obj;
+
+ WARN_ON(!size || (size & ~PAGE_MASK) != 0);
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return NULL;
+
+ drm_gem_private_object_init(ddev, &obj->base, size);
+ return obj;
+}
+
+void sdrm_gem_free_object(struct drm_gem_object *gobj)
+{
+ struct sdrm_gem_object *obj = to_sdrm_bo(gobj);
+ struct drm_device *ddev = gobj->dev;
+
+ if (obj->pages) {
+ /* kill all user-space mappings */
+ drm_vma_node_unmap(&gobj->vma_node, ddev->anon_inode->i_mapping);
+ sdrm_gem_put_pages(obj);
+ }
+
+ if (gobj->import_attach)
+ drm_prime_gem_destroy(gobj, obj->sg);
+
+ drm_gem_free_mmap_offset(gobj);
+ drm_gem_object_release(gobj);
+ kfree(obj);
+}
+
+int sdrm_dumb_create(struct drm_file *dfile, struct drm_device *ddev,
+ struct drm_mode_create_dumb *args)
+{
+ struct sdrm_gem_object *obj;
+ int r;
+
+ if (args->flags)
+ return -EINVAL;
+
+ /* overflow checks are done by DRM core */
+ args->pitch = (args->bpp + 7) / 8 * args->width;
+ args->size = PAGE_ALIGN(args->pitch * args->height);
+
+ obj = sdrm_gem_alloc_object(ddev, args->size);
+ if (!obj)
+ return -ENOMEM;
+
+ r = drm_gem_handle_create(dfile, &obj->base, &args->handle);
+ if (r) {
+ drm_gem_object_unreference(&obj->base);
+ return r;
+ }
+
+ /* handle owns a reference */
+ drm_gem_object_unreference(&obj->base);
+ return 0;
+}
+
+int sdrm_dumb_destroy(struct drm_file *dfile, struct drm_device *ddev,
+ uint32_t handle)
+{
+ return drm_gem_handle_delete(dfile, handle);
+}
+
+int sdrm_dumb_map_offset(struct drm_file *dfile, struct drm_device *ddev,
+ uint32_t handle, uint64_t *offset)
+{
+ struct drm_gem_object *gobj;
+ int r;
+
+ mutex_lock(&ddev->struct_mutex);
+
+ gobj = drm_gem_object_lookup(ddev, dfile, handle);
+ if (!gobj) {
+ r = -ENOENT;
+ goto out_unlock;
+ }
+
+ r = drm_gem_create_mmap_offset(gobj);
+ if (r)
+ goto out_unref;
+
+ *offset = drm_vma_node_offset_addr(&gobj->vma_node);
+
+out_unref:
+ drm_gem_object_unreference(gobj);
+out_unlock:
+ mutex_unlock(&ddev->struct_mutex);
+ return r;
+}
+
+int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_vma_offset_node *node;
+ struct drm_gem_object *gobj;
+ struct sdrm_gem_object *obj;
+ size_t size, i, num;
+ int r;
+
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
+ mutex_lock(&dev->struct_mutex);
+
+ node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
+ vma->vm_pgoff,
+ vma_pages(vma));
+ if (!node) {
+ mutex_unlock(&dev->struct_mutex);
+ return drm_mmap(filp, vma);
+ } else if (!drm_vma_node_is_allowed(node, filp)) {
+ r = -EACCES;
+ goto out_unlock;
+ }
+
+ gobj = container_of(node, struct drm_gem_object, vma_node);
+ obj = to_sdrm_bo(gobj);
+ size = drm_vma_node_size(node) << PAGE_SHIFT;
+ if (size < vma->vm_end - vma->vm_start) {
+ r = -EINVAL;
+ goto out_unlock;
+ }
+
+ r = sdrm_gem_get_pages(obj);
+ if (r < 0)
+ goto out_unlock;
+
+ /* prevent dmabuf-imported mmap to user-space */
+ if (!obj->pages) {
+ r = -EACCES;
+ goto out_unlock;
+ }
+
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+
+ num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ for (i = 0; i < num; ++i) {
+ r = vm_insert_page(vma, vma->vm_start + i * PAGE_SIZE,
+ obj->pages[i]);
+ if (r < 0) {
+ if (i > 0)
+ zap_vma_ptes(vma, vma->vm_start, i * PAGE_SIZE);
+ goto out_unlock;
+ }
+ }
+
+ r = 0;
+
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return r;
+}
+
+struct drm_gem_object *sdrm_gem_prime_import(struct drm_device *ddev,
+ struct dma_buf *dma_buf)
+{
+ struct dma_buf_attachment *attach;
+ struct sdrm_gem_object *obj;
+ struct sg_table *sg;
+ int ret;
+
+ /* need to attach */
+ attach = dma_buf_attach(dma_buf, ddev->dev);
+ if (IS_ERR(attach))
+ return ERR_CAST(attach);
+
+ get_dma_buf(dma_buf);
+
+ sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+ if (IS_ERR(sg)) {
+ ret = PTR_ERR(sg);
+ goto fail_detach;
+ }
+
+ /* dma_buf_vmap() gives us a page-aligned mapping, so lets bump the
+ * size of the dma-buf to the next page-boundary */
+ obj = sdrm_gem_alloc_object(ddev, PAGE_ALIGN(dma_buf->size));
+ if (!obj) {
+ ret = -ENOMEM;
+ goto fail_unmap;
+ }
+
+ obj->sg = sg;
+ obj->base.import_attach = attach;
+
+ return &obj->base;
+
+fail_unmap:
+ dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
+fail_detach:
+ dma_buf_detach(dma_buf, attach);
+ dma_buf_put(dma_buf);
+ return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
new file mode 100644
index 0000000..3e686bb
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c
@@ -0,0 +1,365 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include "simpledrm.h"
+
+/* crtcs */
+
+static int sdrm_crtc_set_config(struct drm_mode_set *set)
+{
+ struct drm_device *ddev;
+ struct sdrm_device *sdrm;
+ struct sdrm_framebuffer *fb;
+
+ if (!set || !set->crtc)
+ return -EINVAL;
+
+ ddev = set->crtc->dev;
+ sdrm = ddev->dev_private;
+
+ if (set->crtc != &sdrm->crtc)
+ return -EINVAL;
+
+ if (!set->mode || !set->fb || !set->num_connectors) {
+ sdrm->conn.encoder = NULL;
+ sdrm->conn.dpms = DRM_MODE_DPMS_OFF;
+ sdrm->enc.crtc = NULL;
+ sdrm->crtc.fb = NULL;
+ sdrm->crtc.enabled = false;
+ return 0;
+ }
+
+ fb = to_sdrm_fb(set->fb);
+
+ if (set->num_connectors != 1 || set->connectors[0] != &sdrm->conn)
+ return -EINVAL;
+ if (set->mode->hdisplay != sdrm->fb_width ||
+ set->mode->vdisplay != sdrm->fb_height)
+ return -EINVAL;
+ if (fb->base.width <= set->x ||
+ fb->base.height <= set->y ||
+ fb->base.width - set->x < sdrm->fb_width ||
+ fb->base.height - set->y < sdrm->fb_height)
+ return -EINVAL;
+
+ sdrm->conn.encoder = &sdrm->enc;
+ sdrm->conn.dpms = DRM_MODE_DPMS_ON;
+ sdrm->enc.crtc = &sdrm->crtc;
+ sdrm->crtc.fb = set->fb;
+ sdrm->crtc.enabled = true;
+ sdrm->crtc.mode = *set->mode;
+ sdrm->crtc.hwmode = *set->mode;
+ sdrm->crtc.x = set->x;
+ sdrm->crtc.y = set->y;
+
+ drm_calc_timestamping_constants(&sdrm->crtc, &sdrm->crtc.hwmode);
+ sdrm_dirty_all_locked(sdrm);
+ return 0;
+}
+
+static const struct drm_crtc_funcs sdrm_crtc_ops = {
+ .set_config = sdrm_crtc_set_config,
+ .destroy = drm_crtc_cleanup,
+};
+
+/* encoders */
+
+static const struct drm_encoder_funcs sdrm_enc_ops = {
+ .destroy = drm_encoder_cleanup,
+};
+
+/* connectors */
+
+static void sdrm_conn_dpms(struct drm_connector *conn, int mode)
+{
+ conn->dpms = mode;
+}
+
+static enum drm_connector_status sdrm_conn_detect(struct drm_connector *conn,
+ bool force)
+{
+ /* We simulate an always connected monitor. simple-fb doesn't
+ * provide any way to detect whether the connector is active. Hence,
+ * signal DRM core that it is always connected. */
+
+ return connector_status_connected;
+}
+
+static int sdrm_conn_fill_modes(struct drm_connector *conn, uint32_t max_x,
+ uint32_t max_y)
+{
+ struct sdrm_device *sdrm = conn->dev->dev_private;
+ struct drm_display_mode *mode;
+ int r;
+
+ if (conn->force = DRM_FORCE_ON)
+ conn->status = connector_status_connected;
+ else if (conn->force)
+ conn->status = connector_status_disconnected;
+ else
+ conn->status = connector_status_connected;
+
+ list_for_each_entry(mode, &conn->modes, head)
+ mode->status = MODE_UNVERIFIED;
+
+ mode = drm_mode_create(sdrm->ddev);
+ if (!mode) {
+ r = 0;
+ } else {
+ /* some values with 10% vsync/hsync to make user-space happy */
+ mode->hdisplay = sdrm->fb_width;
+ mode->hsync_start = mode->hdisplay;
+ mode->hsync_end = mode->hdisplay * 11 / 10;
+ mode->htotal = mode->hsync_end;
+
+ mode->vdisplay = sdrm->fb_height;
+ mode->vsync_start = mode->vdisplay;
+ mode->vsync_end = mode->vdisplay * 11 / 10;
+ mode->vtotal = mode->vsync_end;
+
+ mode->vrefresh = 60;
+ mode->clock = mode->htotal * mode->vtotal * mode->vrefresh;
+ mode->clock /= 1000; /* Hz => kHz */
+ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+ mode->crtc_htotal = mode->htotal;
+ mode->crtc_vtotal = mode->vtotal;
+ mode->crtc_clock = mode->clock;
+
+ drm_mode_set_name(mode);
+ drm_mode_probed_add(conn, mode);
+ drm_mode_connector_list_update(conn);
+ r = 1;
+ }
+
+ if (max_x && max_y)
+ drm_mode_validate_size(conn->dev, &conn->modes,
+ max_x, max_y, 0);
+
+ drm_mode_prune_invalid(conn->dev, &conn->modes, false);
+ if (list_empty(&conn->modes))
+ return 0;
+
+ drm_mode_sort(&conn->modes);
+
+ list_for_each_entry(mode, &conn->modes, head) {
+ mode->vrefresh = drm_mode_vrefresh(mode);
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ }
+
+ return r;
+}
+
+static void sdrm_conn_destroy(struct drm_connector *conn)
+{
+ /* Remove the fake-connector from sysfs and then let the DRM core
+ * clean up all associated resources. */
+ drm_sysfs_connector_remove(conn);
+ drm_connector_cleanup(conn);
+}
+
+static const struct drm_connector_funcs sdrm_conn_ops = {
+ .dpms = sdrm_conn_dpms,
+ .detect = sdrm_conn_detect,
+ .fill_modes = sdrm_conn_fill_modes,
+ .destroy = sdrm_conn_destroy,
+};
+
+/* framebuffers */
+
+static int sdrm_fb_create_handle(struct drm_framebuffer *fb,
+ struct drm_file *dfile,
+ unsigned int *handle)
+{
+ struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
+
+ return drm_gem_handle_create(dfile, &sfb->obj->base, handle);
+}
+
+static void sdrm_fb_destroy(struct drm_framebuffer *fb)
+{
+ struct sdrm_framebuffer *sfb = to_sdrm_fb(fb);
+
+ drm_framebuffer_cleanup(fb);
+ drm_gem_object_unreference_unlocked(&sfb->obj->base);
+ kfree(sfb);
+}
+
+static const struct drm_framebuffer_funcs sdrm_fb_ops = {
+ .create_handle = sdrm_fb_create_handle,
+ .dirty = sdrm_dirty,
+ .destroy = sdrm_fb_destroy,
+};
+
+static struct drm_framebuffer *sdrm_fb_create(struct drm_device *ddev,
+ struct drm_file *dfile,
+ struct drm_mode_fb_cmd2 *cmd)
+{
+ struct sdrm_framebuffer *fb;
+ struct drm_gem_object *gobj;
+ u32 Bpp, size;
+ int ret;
+ void *err;
+
+ if (cmd->flags)
+ return ERR_PTR(-EINVAL);
+ if (cmd->pixel_format != DRM_FORMAT_ARGB8888 &&
+ cmd->pixel_format != DRM_FORMAT_XRGB8888 &&
+ cmd->pixel_format != DRM_FORMAT_RGB565)
+ return ERR_PTR(-EINVAL);
+
+ gobj = drm_gem_object_lookup(ddev, dfile, cmd->handles[0]);
+ if (!gobj)
+ return ERR_PTR(-EINVAL);
+
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+ if (!fb) {
+ err = ERR_PTR(-ENOMEM);
+ goto err_unref;
+ }
+ fb->obj = to_sdrm_bo(gobj);
+
+ fb->base.pitches[0] = cmd->pitches[0];
+ fb->base.offsets[0] = cmd->offsets[0];
+ fb->base.width = cmd->width;
+ fb->base.height = cmd->height;
+ fb->base.pixel_format = cmd->pixel_format;
+ drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth,
+ &fb->base.bits_per_pixel);
+
+ /* width/height are already clamped into min/max_width/height range,
+ * so overflows are not possible */
+
+ Bpp = (fb->base.bits_per_pixel + 7) / 8;
+ size = cmd->pitches[0] * cmd->height;
+ if (!Bpp ||
+ Bpp > 4 ||
+ cmd->pitches[0] < Bpp * fb->base.width ||
+ cmd->pitches[0] > 0xffffU ||
+ size + fb->base.offsets[0] < size ||
+ size + fb->base.offsets[0] > fb->obj->base.size) {
+ err = ERR_PTR(-EINVAL);
+ goto err_free;
+ }
+
+ ret = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops);
+ if (ret < 0) {
+ err = ERR_PTR(ret);
+ goto err_free;
+ }
+
+ return &fb->base;
+
+err_free:
+ kfree(fb);
+err_unref:
+ drm_gem_object_unreference_unlocked(gobj);
+ return err;
+}
+
+static const struct drm_mode_config_funcs sdrm_mode_config_ops = {
+ .fb_create = sdrm_fb_create,
+};
+
+/* initialization */
+
+int sdrm_drm_load(struct drm_device *ddev, unsigned long flags)
+{
+ struct sdrm_device *sdrm;
+ int ret;
+
+ sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL);
+ if (!sdrm)
+ return -ENOMEM;
+
+ platform_set_drvdata(ddev->platformdev, ddev);
+ sdrm->ddev = ddev;
+ ddev->dev_private = sdrm;
+
+ ddev->devname = kstrdup("simpledrm", GFP_KERNEL);
+ if (!ddev->devname) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ ret = sdrm_pdev_init(sdrm);
+ if (ret)
+ goto err_name;
+
+ drm_mode_config_init(ddev);
+ ddev->mode_config.min_width = 1;
+ ddev->mode_config.min_height = 1;
+ ddev->mode_config.max_width = 8192;
+ ddev->mode_config.max_height = 8192;
+ ddev->mode_config.funcs = &sdrm_mode_config_ops;
+
+ ret = drm_crtc_init(ddev, &sdrm->crtc, &sdrm_crtc_ops);
+ if (ret)
+ goto err_cleanup;
+
+ sdrm->enc.possible_crtcs = 1;
+ sdrm->enc.possible_clones = 0;
+ ret = drm_encoder_init(ddev, &sdrm->enc, &sdrm_enc_ops,
+ DRM_MODE_ENCODER_VIRTUAL);
+ if (ret)
+ goto err_cleanup;
+
+ sdrm->conn.display_info.width_mm = 0;
+ sdrm->conn.display_info.height_mm = 0;
+ sdrm->conn.interlace_allowed = false;
+ sdrm->conn.doublescan_allowed = false;
+ sdrm->conn.polled = 0;
+ ret = drm_connector_init(ddev, &sdrm->conn, &sdrm_conn_ops,
+ DRM_MODE_CONNECTOR_VIRTUAL);
+ if (ret)
+ goto err_cleanup;
+
+ ret = drm_mode_connector_attach_encoder(&sdrm->conn, &sdrm->enc);
+ if (ret)
+ goto err_cleanup;
+
+ ret = drm_mode_create_dirty_info_property(ddev);
+ if (ret)
+ goto err_cleanup;
+
+ ret = drm_sysfs_connector_add(&sdrm->conn);
+ if (ret)
+ goto err_cleanup;
+
+ return 0;
+
+err_cleanup:
+ drm_mode_config_cleanup(ddev);
+ sdrm_pdev_destroy(sdrm);
+err_name:
+ kfree(ddev->devname);
+ ddev->devname = NULL;
+err_free:
+ kfree(sdrm);
+ return ret;
+}
+
+int sdrm_drm_unload(struct drm_device *ddev)
+{
+ struct sdrm_device *sdrm = ddev->dev_private;
+
+ drm_mode_config_cleanup(ddev);
+ sdrm_pdev_destroy(sdrm);
+ kfree(sdrm);
+
+ return 0;
+}
--
1.8.5.3
^ permalink raw reply related
* [PATCH 10/11] drm: simpledrm: add fbdev fallback support
From: David Herrmann @ 2014-01-23 14:15 UTC (permalink / raw)
To: dri-devel
Cc: linux-fbdev, Daniel Vetter, linux-kernel, Tomi Valkeinen,
Ingo Molnar
In-Reply-To: <1390486503-1504-1-git-send-email-dh.herrmann@gmail.com>
Create a simple fbdev device during SimpleDRM setup so legacy user-space
and fbcon can use it.
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
---
drivers/gpu/drm/simpledrm/Kconfig | 11 +++
drivers/gpu/drm/simpledrm/Makefile | 1 +
drivers/gpu/drm/simpledrm/simpledrm.c | 13 ++-
drivers/gpu/drm/simpledrm/simpledrm.h | 22 +++++
drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 148 ++++++++++++++++++++++++++++
5 files changed, 194 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig
index 35bcce8..eef2a36 100644
--- a/drivers/gpu/drm/simpledrm/Kconfig
+++ b/drivers/gpu/drm/simpledrm/Kconfig
@@ -12,7 +12,18 @@ config DRM_SIMPLEDRM
SimpleDRM supports "simple-framebuffer" DeviceTree objects and
compatible platform framebuffers.
+ If fbdev support is enabled, this driver will also provide an fbdev
+ compatibility layer.
+
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called simpledrm.
+
+config DRM_SIMPLEDRM_FBDEV
+ bool
+ depends on DRM_SIMPLEDRM && FB
+ default y
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile
index 35f73ea..363b17d 100644
--- a/drivers/gpu/drm/simpledrm/Makefile
+++ b/drivers/gpu/drm/simpledrm/Makefile
@@ -1,3 +1,4 @@
sdrm-y := simpledrm.o simpledrm_kms.o simpledrm_gem.o simpledrm_damage.o
+sdrm-$(CONFIG_DRM_SIMPLEDRM_FBDEV) += simpledrm_fbdev.o
obj-$(CONFIG_DRM_SIMPLEDRM) := sdrm.o
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.c b/drivers/gpu/drm/simpledrm/simpledrm.c
index 98273f5..4346bcd 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm.c
+++ b/drivers/gpu/drm/simpledrm/simpledrm.c
@@ -192,7 +192,17 @@ void sdrm_pdev_destroy(struct sdrm_device *sdrm)
static int sdrm_simplefb_probe(struct platform_device *pdev)
{
- return drm_platform_init(&sdrm_drm_driver, pdev);
+ struct drm_device *ddev;
+ int r;
+
+ r = drm_platform_init(&sdrm_drm_driver, pdev);
+ if (r < 0)
+ return r;
+
+ ddev = platform_get_drvdata(pdev);
+ sdrm_fbdev_init(ddev->dev_private);
+
+ return 0;
}
static int sdrm_simplefb_remove(struct platform_device *pdev)
@@ -205,6 +215,7 @@ static int sdrm_simplefb_remove(struct platform_device *pdev)
* CPU is currently doing some GEM-bo access in parallel to us. But, eh,
* what can we do.. Fixes for that is being worked on. */
+ sdrm_fbdev_cleanup(sdrm);
drm_connector_unplug_all(ddev);
/* protect fb_map removal against sdrm_blit() */
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h
index 4ece7f5..7050594 100644
--- a/drivers/gpu/drm/simpledrm/simpledrm.h
+++ b/drivers/gpu/drm/simpledrm/simpledrm.h
@@ -45,6 +45,9 @@ struct sdrm_device {
struct drm_encoder enc;
struct drm_connector conn;
struct drm_display_mode *mode;
+
+ /* fbdev */
+ struct fb_info *fbdev;
};
int sdrm_drm_load(struct drm_device *ddev, unsigned long flags);
@@ -97,4 +100,23 @@ struct sdrm_framebuffer {
#define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base)
+/* simpledrm fbdev helpers */
+
+#ifdef CONFIG_DRM_SIMPLEDRM_FBDEV
+
+void sdrm_fbdev_init(struct sdrm_device *sdrm);
+void sdrm_fbdev_cleanup(struct sdrm_device *sdrm);
+
+#else /* CONFIG_DRM_SIMPLEDRM_FBDEV */
+
+static inline void sdrm_fbdev_init(struct sdrm_device *sdrm)
+{
+}
+
+static inline void sdrm_fbdev_cleanup(struct sdrm_device *sdrm)
+{
+}
+
+#endif /* CONFIG_DRM_SIMPLEDRM_FBDEV */
+
#endif /* SDRM_DRV_H */
diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
new file mode 100644
index 0000000..5df1482
--- /dev/null
+++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
@@ -0,0 +1,148 @@
+/*
+ * SimpleDRM firmware framebuffer driver
+ * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * fbdev compatibility layer
+ * We provide a basic fbdev device for the same framebuffer that is used for
+ * the pseudo CRTC.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include "simpledrm.h"
+
+struct sdrm_fbdev {
+ u32 palette[16];
+};
+
+static int sdrm_fbdev_setcolreg(u_int regno, u_int red, u_int green,
+ u_int blue, u_int transp, struct fb_info *info)
+{
+ u32 *pal = info->pseudo_palette;
+ u32 cr = red >> (16 - info->var.red.length);
+ u32 cg = green >> (16 - info->var.green.length);
+ u32 cb = blue >> (16 - info->var.blue.length);
+ u32 value;
+
+ if (regno >= 16)
+ return -EINVAL;
+
+ value = (cr << info->var.red.offset) |
+ (cg << info->var.green.offset) |
+ (cb << info->var.blue.offset);
+
+ if (info->var.transp.length > 0) {
+ u32 mask = (1 << info->var.transp.length) - 1;
+ mask <<= info->var.transp.offset;
+ value |= mask;
+ }
+
+ pal[regno] = value;
+
+ return 0;
+}
+
+static struct fb_ops sdrm_fbdev_ops = {
+ .owner = THIS_MODULE,
+ .fb_setcolreg = sdrm_fbdev_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+void sdrm_fbdev_init(struct sdrm_device *sdrm)
+{
+ struct sdrm_fbdev *fb;
+ struct fb_info *info;
+ int ret;
+
+ if (fb_get_options("simpledrmfb", NULL))
+ return;
+
+ info = framebuffer_alloc(sizeof(struct sdrm_fbdev), sdrm->ddev->dev);
+ if (!info)
+ goto err_out;
+
+ fb = info->par;
+ info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE;
+ info->pseudo_palette = fb->palette;
+ info->fbops = &sdrm_fbdev_ops;
+ info->screen_base = sdrm->fb_map;
+
+ strncpy(info->fix.id, "simpledrmfb", 15);
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fix.smem_start = (unsigned long)sdrm->fb_base;
+ info->fix.smem_len = sdrm->fb_size;
+ info->fix.line_length = sdrm->fb_stride;
+
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.vmode = FB_VMODE_NONINTERLACED;
+ info->var.bits_per_pixel = sdrm->fb_bpp;
+ info->var.height = -1;
+ info->var.width = -1;
+ info->var.xres = sdrm->fb_width;
+ info->var.yres = sdrm->fb_height;
+ info->var.xres_virtual = info->var.xres;
+ info->var.yres_virtual = info->var.yres;
+ info->var.red = sdrm->fb_sformat->red;
+ info->var.green = sdrm->fb_sformat->green;
+ info->var.blue = sdrm->fb_sformat->blue;
+ info->var.transp = sdrm->fb_sformat->transp;
+
+ /* some dummy values for timing to make fbset happy */
+ info->var.pixclock = 10000000 / info->var.xres * 1000 / info->var.yres;
+ info->var.left_margin = (info->var.xres / 8) & 0xf8;
+ info->var.right_margin = 32;
+ info->var.upper_margin = 16;
+ info->var.lower_margin = 4;
+ info->var.hsync_len = (info->var.xres / 8) & 0xf8;
+ info->var.vsync_len = 4;
+
+ sdrm->fbdev = info;
+ ret = register_framebuffer(info);
+ if (ret < 0)
+ goto err_free;
+
+ dev_info(sdrm->ddev->dev, "fbdev frontend %s as fb%d\n",
+ info->fix.id, info->node);
+
+ return;
+
+err_free:
+ framebuffer_release(info);
+ sdrm->fbdev = NULL;
+err_out:
+ dev_warn(sdrm->ddev->dev, "cannot create fbdev frontend\n");
+}
+
+void sdrm_fbdev_cleanup(struct sdrm_device *sdrm)
+{
+ struct fb_info *info;
+
+ if (!sdrm->fbdev)
+ return;
+
+ info = sdrm->fbdev;
+ sdrm->fbdev = NULL;
+
+ dev_info(sdrm->ddev->dev, "remove fbdev frontend %s (fb%d)\n",
+ info->fix.id, info->node);
+
+ if (unregister_framebuffer(info))
+ dev_err(sdrm->ddev->dev, "unregister_framebuffer() failed, leaking fw-fb\n");
+ else
+ framebuffer_release(info);
+}
--
1.8.5.3
^ permalink raw reply related
* [PATCH 11/11] x86/sysfb: allow sysfb+simpledrm combination
From: David Herrmann @ 2014-01-23 14:15 UTC (permalink / raw)
To: dri-devel
Cc: linux-fbdev, Daniel Vetter, linux-kernel, Tomi Valkeinen,
Ingo Molnar
In-Reply-To: <1390486503-1504-1-git-send-email-dh.herrmann@gmail.com>
We used to protect X86_SYSFB by depending on FB_SIMPLE so users don't
accidentally end up without a kernel console. Now that DRM_SIMPLEDRM also
provides a simple-framebuffer driver, we can whitelist this driver, too.
---
arch/x86/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 93df439..f3eae6a 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -2299,7 +2299,7 @@ source "drivers/rapidio/Kconfig"
config X86_SYSFB
bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
- depends on FB_SIMPLE
+ depends on FB_SIMPLE || DRM_SIMPLEDRM
select SYSFB
help
Firmwares often provide initial graphics framebuffers so the BIOS,
--
1.8.5.3
^ permalink raw reply related
* Re: [PATCH 02/11] x86: sysfb: remove sysfb when probing real hw
From: Ingo Molnar @ 2014-01-23 16:51 UTC (permalink / raw)
To: David Herrmann
Cc: dri-devel, linux-fbdev, Dave Airlie, Daniel Vetter,
Tomi Valkeinen, linux-kernel, Tom Gundersen
In-Reply-To: <1390486503-1504-3-git-send-email-dh.herrmann@gmail.com>
Just a couple of small nits:
* David Herrmann <dh.herrmann@gmail.com> wrote:
> --- a/arch/x86/kernel/sysfb.c
> +++ b/arch/x86/kernel/sysfb.c
> @@ -33,11 +33,76 @@
> #include <linux/init.h>
> #include <linux/kernel.h>
> #include <linux/mm.h>
> +#include <linux/mutex.h>
> #include <linux/platform_data/simplefb.h>
> #include <linux/platform_device.h>
> #include <linux/screen_info.h>
> #include <asm/sysfb.h>
>
> +static DEFINE_MUTEX(sysfb_lock);
> +static struct platform_device *sysfb_dev;
> +
> +int __init sysfb_register(const char *name, int id,
> + const struct resource *res, unsigned int res_num,
> + const void *data, size_t data_size)
> +{
> + struct platform_device *pd;
> + int ret = 0;
> +
> + mutex_lock(&sysfb_lock);
> + if (!sysfb_dev) {
> + pd = platform_device_register_resndata(NULL, name, id,
> + res, res_num,
> + data, data_size);
> + if (IS_ERR(pd))
> + ret = PTR_ERR(pd);
> + else
> + sysfb_dev = pd;
> + }
> + mutex_unlock(&sysfb_lock);
> +
> + return ret;
> +}
> +
> +static bool sysfb_match(const struct apertures_struct *apert)
> +{
> + struct screen_info *si = &screen_info;
> + unsigned int i;
> + const struct aperture *a;
> +
> + for (i = 0; i < apert->count; ++i) {
> + a = &apert->ranges[i];
> + if (a->base >= si->lfb_base &&
> + a->base < si->lfb_base + ((u64)si->lfb_size << 16))
> + return true;
> + if (si->lfb_base >= a->base &&
> + si->lfb_base < a->base + a->size)
> + return true;
> + }
> +
> + return false;
> +}
> +
> +/* Remove sysfb and disallow new sysfbs from now on. Can be called from any
> + * context except recursively (see also remove_conflicting_framebuffers()). */
> +void sysfb_unregister(const struct apertures_struct *apert, bool primary)
Please use the customary (multi-line) comment style:
/*
* Comment .....
* ...... goes here.
*/
specified in Documentation/CodingStyle.
> +#ifdef CONFIG_X86_SYSFB
> +# include <asm/sysfb.h>
> +#endif
I guess a single space is sufficient?
Better yet, I'd include sysfb.h unconditionally:
> @@ -1773,6 +1780,10 @@ register_framebuffer(struct fb_info *fb_info)
> {
> int ret;
>
> +#ifdef CONFIG_X86_SYSFB
> + sysfb_unregister(fb_info->apertures, fb_is_primary_device(fb_info));
> +#endif
So, if a dummy sysfb_unregister() inline was defined in the
!CONFIG_X86_SYSFB case then this ugly #ifdef could possibly be
removed? Especially as it's used twice.
Thanks,
Ingo
^ permalink raw reply
* Re: [PATCH 02/11] x86: sysfb: remove sysfb when probing real hw
From: David Herrmann @ 2014-01-23 17:07 UTC (permalink / raw)
To: Ingo Molnar
Cc: linux-fbdev@vger.kernel.org, Daniel Vetter, linux-kernel,
dri-devel@lists.freedesktop.org, Tomi Valkeinen
In-Reply-To: <20140123165115.GB23869@gmail.com>
Hi
On Thu, Jan 23, 2014 at 5:51 PM, Ingo Molnar <mingo@kernel.org> wrote:
>
> Just a couple of small nits:
>
> * David Herrmann <dh.herrmann@gmail.com> wrote:
>
>> --- a/arch/x86/kernel/sysfb.c
>> +++ b/arch/x86/kernel/sysfb.c
>> @@ -33,11 +33,76 @@
>> #include <linux/init.h>
>> #include <linux/kernel.h>
>> #include <linux/mm.h>
>> +#include <linux/mutex.h>
>> #include <linux/platform_data/simplefb.h>
>> #include <linux/platform_device.h>
>> #include <linux/screen_info.h>
>> #include <asm/sysfb.h>
>>
>> +static DEFINE_MUTEX(sysfb_lock);
>> +static struct platform_device *sysfb_dev;
>> +
>> +int __init sysfb_register(const char *name, int id,
>> + const struct resource *res, unsigned int res_num,
>> + const void *data, size_t data_size)
>> +{
>> + struct platform_device *pd;
>> + int ret = 0;
>> +
>> + mutex_lock(&sysfb_lock);
>> + if (!sysfb_dev) {
>> + pd = platform_device_register_resndata(NULL, name, id,
>> + res, res_num,
>> + data, data_size);
>> + if (IS_ERR(pd))
>> + ret = PTR_ERR(pd);
>> + else
>> + sysfb_dev = pd;
>> + }
>> + mutex_unlock(&sysfb_lock);
>> +
>> + return ret;
>> +}
>> +
>> +static bool sysfb_match(const struct apertures_struct *apert)
>> +{
>> + struct screen_info *si = &screen_info;
>> + unsigned int i;
>> + const struct aperture *a;
>> +
>> + for (i = 0; i < apert->count; ++i) {
>> + a = &apert->ranges[i];
>> + if (a->base >= si->lfb_base &&
>> + a->base < si->lfb_base + ((u64)si->lfb_size << 16))
>> + return true;
>> + if (si->lfb_base >= a->base &&
>> + si->lfb_base < a->base + a->size)
>> + return true;
>> + }
>> +
>> + return false;
>> +}
>> +
>> +/* Remove sysfb and disallow new sysfbs from now on. Can be called from any
>> + * context except recursively (see also remove_conflicting_framebuffers()). */
>> +void sysfb_unregister(const struct apertures_struct *apert, bool primary)
>
> Please use the customary (multi-line) comment style:
>
> /*
> * Comment .....
> * ...... goes here.
> */
>
> specified in Documentation/CodingStyle.
Whoops, will fix it up. Still used to that from HID code.
>> +#ifdef CONFIG_X86_SYSFB
>> +# include <asm/sysfb.h>
>> +#endif
>
> I guess a single space is sufficient?
>
> Better yet, I'd include sysfb.h unconditionally:
Unconditionally won't work as only x86 has this header. If there's a
way to place a dummy into asm-generic which is picked if
arch/xy/include/asm/ doesn't have the header, let me know. But if I
include it unconditionally without any fallback, this will fail on
non-x86.
And adding the header to all archs seems overkill.
>> @@ -1773,6 +1780,10 @@ register_framebuffer(struct fb_info *fb_info)
>> {
>> int ret;
>>
>> +#ifdef CONFIG_X86_SYSFB
>> + sysfb_unregister(fb_info->apertures, fb_is_primary_device(fb_info));
>> +#endif
>
> So, if a dummy sysfb_unregister() inline was defined in the
> !CONFIG_X86_SYSFB case then this ugly #ifdef could possibly be
> removed? Especially as it's used twice.
Again, this is fine for x86, but not for other archs. I would still
need the #ifdef x86.
Note that patch #6 introduces linux/sysfb.h and removes all these ugly
#ifdefs again. They're only needed to fix the x86 code *now*. Patch #6
generalizes the x86-sysfb infrastructure and makes it
arch-independent. But Patch #6 introduces new features and thus
shouldn't go to stable or 3.14.
As Patch #1 already fixes nearly all issues with sysfb, let me know if
you want to drop this patch and just wait for the arch-independent
sysfb to get merged. This patch is only needed if people enable
X86_SYSFB *and* FB_SIMPLE *purposely* and want hw-handover. The case
were people enable it accidentally is fixed by Patch #1.
The situation is kind of screwed.. sorry for that.
Thanks
David
^ permalink raw reply
* Re: [PATCH 02/11] x86: sysfb: remove sysfb when probing real hw
From: Ingo Molnar @ 2014-01-23 17:14 UTC (permalink / raw)
To: David Herrmann
Cc: dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org,
Dave Airlie, Daniel Vetter, Tomi Valkeinen, linux-kernel,
Tom Gundersen
In-Reply-To: <CANq1E4QQE2qeEz1AaN1NO3p2TE006jHfz8uaJ_LLHptKdHecxQ@mail.gmail.com>
* David Herrmann <dh.herrmann@gmail.com> wrote:
> >> +#ifdef CONFIG_X86_SYSFB
> >> +# include <asm/sysfb.h>
> >> +#endif
> >
> > I guess a single space is sufficient?
> >
> > Better yet, I'd include sysfb.h unconditionally:
>
> Unconditionally won't work as only x86 has this header. [...]
Well, in non-x86 code an #ifdef x86 looks ugly as well - but I guess
better than not building.
> [...] If there's a way to place a dummy into asm-generic which is
> picked if arch/xy/include/asm/ doesn't have the header, let me know.
Not that I know of.
> But if I include it unconditionally without any fallback, this will
> fail on non-x86. And adding the header to all archs seems overkill.
So why not drop the x86-ism and rename it to CONFIG_PLATFORM_SYSFB?
Some platforms configure it, some don't. Then the prototypes could
move into include/linux/sysfb.h or so and would be platform agnostic.
Thanks,
Ingo
^ permalink raw reply
* Re: [PATCH 02/11] x86: sysfb: remove sysfb when probing real hw
From: David Herrmann @ 2014-01-23 19:09 UTC (permalink / raw)
To: Ingo Molnar
Cc: linux-fbdev@vger.kernel.org, Daniel Vetter, linux-kernel,
dri-devel@lists.freedesktop.org, Tomi Valkeinen
In-Reply-To: <20140123171436.GA27345@gmail.com>
Hi
On Thu, Jan 23, 2014 at 6:14 PM, Ingo Molnar <mingo@kernel.org> wrote:
>
> * David Herrmann <dh.herrmann@gmail.com> wrote:
>
>> >> +#ifdef CONFIG_X86_SYSFB
>> >> +# include <asm/sysfb.h>
>> >> +#endif
>> >
>> > I guess a single space is sufficient?
>> >
>> > Better yet, I'd include sysfb.h unconditionally:
>>
>> Unconditionally won't work as only x86 has this header. [...]
>
> Well, in non-x86 code an #ifdef x86 looks ugly as well - but I guess
> better than not building.
>
>> [...] If there's a way to place a dummy into asm-generic which is
>> picked if arch/xy/include/asm/ doesn't have the header, let me know.
>
> Not that I know of.
>
>> But if I include it unconditionally without any fallback, this will
>> fail on non-x86. And adding the header to all archs seems overkill.
>
> So why not drop the x86-ism and rename it to CONFIG_PLATFORM_SYSFB?
> Some platforms configure it, some don't. Then the prototypes could
> move into include/linux/sysfb.h or so and would be platform agnostic.
This is almost exactly what patch #6 does. But it also adds ~400 lines
of kernel-doc and ~400 lines of Documentation/. Given your remarks, I
guess I will just split this patch into code and docs, so we can just
pick it up for stable in case patch #1 does not fix all issues.
Thanks
David
^ permalink raw reply
* Re: [PATCH 4/10] atyfb: set FBINFO_READS_FAST
From: Linus Torvalds @ 2014-01-23 19:23 UTC (permalink / raw)
To: Mikulas Patocka, linux-fbdev@vger.kernel.org, Tomi Valkeinen
Cc: Linux Kernel Mailing List
In-Reply-To: <alpine.LRH.2.02.1401231305390.31739@file01.intranet.prod.int.rdu2.redhat.com>
Hmm. You're doing this for Matrix and now the aty driver.
Maybe the problem is at the fbcon level? Depending on
FBINFO_READS_FAST to decide whether you should scroll or rewrite
sounds a bit silly: even if a device doesn't have fast reads, maybe it
has a fast accelerated BLIT operation and scrolls quickly. Should the
fbcon test perhaps be for FBINFO_READS_FAST _or_ the
FBINFO_HWACCEL_COPYAREA bits?
I dunno. I didn't actually check the ->bcopy implementations, maybe
they don't use copyarea. So I'm just going by a general "this feels
wrong" feeling..
Linus
On Thu, Jan 23, 2014 at 10:18 AM, Mikulas Patocka <mpatocka@redhat.com> wrote:
> Set FBINFO_READS_FAST so that the console code uses scrolling instead of
> rewriting. This improves scrolling speed.
^ permalink raw reply
* Re: [PATCH 4/10] atyfb: set FBINFO_READS_FAST
From: Mikulas Patocka @ 2014-01-23 19:31 UTC (permalink / raw)
To: Linus Torvalds
Cc: linux-fbdev@vger.kernel.org, Tomi Valkeinen,
Linux Kernel Mailing List
In-Reply-To: <CA+55aFxPfHmpcMH-bmRcTPUQeq5=67cKVmra6+SvT26ybvQaug@mail.gmail.com>
On Thu, 23 Jan 2014, Linus Torvalds wrote:
> Hmm. You're doing this for Matrix and now the aty driver.
>
> Maybe the problem is at the fbcon level? Depending on
> FBINFO_READS_FAST to decide whether you should scroll or rewrite
> sounds a bit silly: even if a device doesn't have fast reads, maybe it
> has a fast accelerated BLIT operation and scrolls quickly. Should the
> fbcon test perhaps be for FBINFO_READS_FAST _or_ the
> FBINFO_HWACCEL_COPYAREA bits?
Both mach64 and matrox have a hardware bitter that is faster than
rewriting the console - that's why FBINFO_READS_FAST improves performance
for them.
One could improve generic framebuffer core to benchmark the performance of
scrolling using blitter and rewrite and select FBINFO_READS_FAST
automatically...
Mikulas
> I dunno. I didn't actually check the ->bcopy implementations, maybe
> they don't use copyarea. So I'm just going by a general "this feels
> wrong" feeling..
>
> Linus
> On Thu, Jan 23, 2014 at 10:18 AM, Mikulas Patocka <mpatocka@redhat.com> wrote:
> > Set FBINFO_READS_FAST so that the console code uses scrolling instead of
> > rewriting. This improves scrolling speed.
>
^ permalink raw reply
* [PATCH 0/10] framebuffer patches
From: Mikulas Patocka @ 2014-01-23 19:37 UTC (permalink / raw)
To: linux-fbdev
Hi
Here I'm sending some framebuffer patches for matrox, mach64, tga and a
fix for copying on vesafb.
Mikulas
^ permalink raw reply
* [PATCH 1/10] matroxfb: set FBINFO_READS_FAST
From: Mikulas Patocka @ 2014-01-23 19:38 UTC (permalink / raw)
To: linux-fbdev
Set FBINFO_READS_FAST so that the console code uses scrolling instead of
rewriting. This improves scrolling speed.
A time to do ls -la /usr/bin:
original patched
32bpp 5.4 3.6
24bpp 5.1 3.0
16bpp 4.9 2.5
8bpp 4.9 2.0
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
---
drivers/video/matrox/matroxfb_base.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Index: linux-3.12.1/drivers/video/matrox/matroxfb_base.c
=================================--- linux-3.12.1.orig/drivers/video/matrox/matroxfb_base.c 2013-11-18 18:14:25.000000000 +0100
+++ linux-3.12.1/drivers/video/matrox/matroxfb_base.c 2013-11-23 17:58:47.000000000 +0100
@@ -1773,7 +1773,8 @@ static int initMatrox2(struct matrox_fb_
FBINFO_HWACCEL_FILLRECT | /* And fillrect */
FBINFO_HWACCEL_IMAGEBLIT | /* And imageblit */
FBINFO_HWACCEL_XPAN | /* And we support both horizontal */
- FBINFO_HWACCEL_YPAN; /* And vertical panning */
+ FBINFO_HWACCEL_YPAN | /* And vertical panning */
+ FBINFO_READS_FAST;
minfo->video.len_usable &= PAGE_MASK;
fb_alloc_cmap(&minfo->fbcon.cmap, 256, 1);
^ permalink raw reply
* [PATCH 2/10] matroxfb: restore the registers M_ACCESS and M_PITCH
From: Mikulas Patocka @ 2014-01-23 19:39 UTC (permalink / raw)
To: linux-fbdev
When X11 is running and the user switches back to console, the card
modifies the content of registers M_MACCESS and M_PITCH in periodic
intervals.
This patch fixes it by restoring the content of these registers before
issuing any accelerator command.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: stable@vger.kernel.org
---
drivers/video/matrox/matroxfb_accel.c | 38 +++++++++++++++++++++++++---------
drivers/video/matrox/matroxfb_base.h | 2 +
2 files changed, 30 insertions(+), 10 deletions(-)
Index: linux-3.12.5/drivers/video/matrox/matroxfb_accel.c
=================================--- linux-3.12.5.orig/drivers/video/matrox/matroxfb_accel.c 2013-12-15 16:32:04.056383894 +0100
+++ linux-3.12.5/drivers/video/matrox/matroxfb_accel.c 2013-12-15 16:33:10.271706929 +0100
@@ -192,10 +192,18 @@ void matrox_cfbX_init(struct matrox_fb_i
minfo->accel.m_dwg_rect = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO;
if (isMilleniumII(minfo)) minfo->accel.m_dwg_rect |= M_DWG_TRANSC;
minfo->accel.m_opmode = mopmode;
+ minfo->accel.m_access = maccess;
+ minfo->accel.m_pitch = mpitch;
}
EXPORT_SYMBOL(matrox_cfbX_init);
+static void matrox_accel_restore_maccess(struct matrox_fb_info *minfo)
+{
+ mga_outl(M_MACCESS, minfo->accel.m_access);
+ mga_outl(M_PITCH, minfo->accel.m_pitch);
+}
+
static void matrox_accel_bmove(struct matrox_fb_info *minfo, int vxres, int sy,
int sx, int dy, int dx, int height, int width)
{
@@ -207,7 +215,8 @@ static void matrox_accel_bmove(struct ma
CRITBEGIN
if ((dy < sy) || ((dy = sy) && (dx <= sx))) {
- mga_fifo(2);
+ mga_fifo(4);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO |
M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_AR5, vxres);
@@ -215,7 +224,8 @@ static void matrox_accel_bmove(struct ma
start = sy*vxres+sx+curr_ydstorg(minfo);
end = start+width;
} else {
- mga_fifo(3);
+ mga_fifo(5);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_SGN, 5);
mga_outl(M_AR5, -vxres);
@@ -224,7 +234,8 @@ static void matrox_accel_bmove(struct ma
start = end+width;
dy += height-1;
}
- mga_fifo(4);
+ mga_fifo(6);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_AR0, end);
mga_outl(M_AR3, start);
mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx);
@@ -246,7 +257,8 @@ static void matrox_accel_bmove_lin(struc
CRITBEGIN
if ((dy < sy) || ((dy = sy) && (dx <= sx))) {
- mga_fifo(2);
+ mga_fifo(4);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO |
M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_AR5, vxres);
@@ -254,7 +266,8 @@ static void matrox_accel_bmove_lin(struc
start = sy*vxres+sx+curr_ydstorg(minfo);
end = start+width;
} else {
- mga_fifo(3);
+ mga_fifo(5);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE);
mga_outl(M_SGN, 5);
mga_outl(M_AR5, -vxres);
@@ -263,7 +276,8 @@ static void matrox_accel_bmove_lin(struc
start = end+width;
dy += height-1;
}
- mga_fifo(5);
+ mga_fifo(7);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_AR0, end);
mga_outl(M_AR3, start);
mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx);
@@ -298,7 +312,8 @@ static void matroxfb_accel_clear(struct
CRITBEGIN
- mga_fifo(5);
+ mga_fifo(7);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE);
mga_outl(M_FCOL, color);
mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx);
@@ -341,7 +356,8 @@ static void matroxfb_cfb4_clear(struct m
width >>= 1;
sx >>= 1;
if (width) {
- mga_fifo(5);
+ mga_fifo(7);
+ matrox_accel_restore_maccess(minfo);
mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE2);
mga_outl(M_FCOL, bgx);
mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx);
@@ -415,7 +431,8 @@ static void matroxfb_1bpp_imageblit(stru
CRITBEGIN
- mga_fifo(3);
+ mga_fifo(5);
+ matrox_accel_restore_maccess(minfo);
if (easy)
mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE);
else
@@ -425,7 +442,8 @@ static void matroxfb_1bpp_imageblit(stru
fxbndry = ((xx + width - 1) << 16) | xx;
mmio = minfo->mmio.vbase;
- mga_fifo(6);
+ mga_fifo(8);
+ matrox_accel_restore_maccess(minfo);
mga_writel(mmio, M_FXBNDRY, fxbndry);
mga_writel(mmio, M_AR0, ar0);
mga_writel(mmio, M_AR3, 0);
Index: linux-3.12.5/drivers/video/matrox/matroxfb_base.h
=================================--- linux-3.12.5.orig/drivers/video/matrox/matroxfb_base.h 2013-12-15 16:31:56.823877143 +0100
+++ linux-3.12.5/drivers/video/matrox/matroxfb_base.h 2013-12-15 16:33:10.275039841 +0100
@@ -307,6 +307,8 @@ struct matrox_accel_data {
#endif
u_int32_t m_dwg_rect;
u_int32_t m_opmode;
+ u_int32_t m_access;
+ u_int32_t m_pitch;
};
struct v4l2_queryctrl;
^ permalink raw reply
* [PATCH 3/10] framebuffer: fix cfb_copyarea
From: Mikulas Patocka @ 2014-01-23 19:39 UTC (permalink / raw)
To: linux-fbdev
The function cfb_copyarea is buggy when the copy operation is not aligned on
long boundary (4 bytes on 32-bit machines, 8 bytes on 64-bit machines).
How to reproduce:
- use x86-64 machine
- use a framebuffer driver without acceleration (for example uvesafb)
- set the framebuffer to 8-bit depth
(for example fbset -a 1024x768-60 -depth 8)
- load a font with character width that is not a multiple of 8 pixels
note: the console-tools package cannot load a font that has
width different from 8 pixels. You need to install the packages
"kbd" and "console-terminus" and use the program "setfont" to
set font width (for example: setfont Uni2-Terminus20x10)
- move some text left and right on the bash command line and you get a
screen corruption
To expose more bugs, put this line to the end of uvesafb_init_info:
info->flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_READS_FAST;
- Now framebuffer console will use cfb_copyarea for console scrolling.
You get a screen corruption when console is scrolled.
This patch is a rewrite of cfb_copyarea. It fixes the bugs, with this
patch, console scrolling in 8-bit depth with a font width that is not a
multiple of 8 pixels works fine.
The cfb_copyarea code was very buggy and it looks like it was written
and never tried with non-8-pixel font.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: stable@vger.kernel.org
---
drivers/video/cfbcopyarea.c | 153 ++++++++++++++++++++++----------------------
1 file changed, 78 insertions(+), 75 deletions(-)
Index: linux-3.4.3-fast/drivers/video/cfbcopyarea.c
=================================--- linux-3.4.3-fast.orig/drivers/video/cfbcopyarea.c 2012-07-02 02:02:08.000000000 +0200
+++ linux-3.4.3-fast/drivers/video/cfbcopyarea.c 2012-07-02 02:03:33.000000000 +0200
@@ -43,13 +43,22 @@
*/
static void
-bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
- const unsigned long __iomem *src, int src_idx, int bits,
+bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
+ const unsigned long __iomem *src, unsigned src_idx, int bits,
unsigned n, u32 bswapmask)
{
unsigned long first, last;
int const shift = dst_idx-src_idx;
- int left, right;
+
+#if 0
+ /*
+ * If you suspect bug in this function, compare it with this simple
+ * memmove implementation.
+ */
+ fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
+ (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
+ return;
+#endif
first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
@@ -98,9 +107,8 @@ bitcpy(struct fb_info *p, unsigned long
unsigned long d0, d1;
int m;
- right = shift & (bits - 1);
- left = -shift & (bits - 1);
- bswapmask &= shift;
+ int const left = shift & (bits - 1);
+ int const right = -shift & (bits - 1);
if (dst_idx+n <= bits) {
// Single destination word
@@ -110,15 +118,15 @@ bitcpy(struct fb_info *p, unsigned long
d0 = fb_rev_pixels_in_long(d0, bswapmask);
if (shift > 0) {
// Single source word
- d0 >>= right;
+ d0 <<= left;
} else if (src_idx+n <= bits) {
// Single source word
- d0 <<= left;
+ d0 >>= right;
} else {
// 2 source words
d1 = FB_READL(src + 1);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0<<left | d1>>right;
+ d0 = d0 >> right | d1 << left;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
@@ -135,60 +143,59 @@ bitcpy(struct fb_info *p, unsigned long
if (shift > 0) {
// Single source word
d1 = d0;
- d0 >>= right;
- dst++;
+ d0 <<= left;
n -= bits - dst_idx;
} else {
// 2 source words
d1 = FB_READL(src++);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0<<left | d1>>right;
- dst++;
+ d0 = d0 >> right | d1 << left;
n -= bits - dst_idx;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
d0 = d1;
+ dst++;
// Main chunk
m = n % bits;
n /= bits;
while ((n >= 4) && !bswapmask) {
d1 = FB_READL(src++);
- FB_WRITEL(d0 << left | d1 >> right, dst++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
d0 = d1;
d1 = FB_READL(src++);
- FB_WRITEL(d0 << left | d1 >> right, dst++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
d0 = d1;
d1 = FB_READL(src++);
- FB_WRITEL(d0 << left | d1 >> right, dst++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
d0 = d1;
d1 = FB_READL(src++);
- FB_WRITEL(d0 << left | d1 >> right, dst++);
+ FB_WRITEL(d0 >> right | d1 << left, dst++);
d0 = d1;
n -= 4;
}
while (n--) {
d1 = FB_READL(src++);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0 << left | d1 >> right;
+ d0 = d0 >> right | d1 << left;
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(d0, dst++);
d0 = d1;
}
// Trailing bits
- if (last) {
- if (m <= right) {
+ if (m) {
+ if (m <= bits - right) {
// Single source word
- d0 <<= left;
+ d0 >>= right;
} else {
// 2 source words
d1 = FB_READL(src);
d1 = fb_rev_pixels_in_long(d1,
bswapmask);
- d0 = d0<<left | d1>>right;
+ d0 = d0 >> right | d1 << left;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
@@ -202,43 +209,46 @@ bitcpy(struct fb_info *p, unsigned long
*/
static void
-bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, int dst_idx,
- const unsigned long __iomem *src, int src_idx, int bits,
+bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
+ const unsigned long __iomem *src, unsigned src_idx, int bits,
unsigned n, u32 bswapmask)
{
unsigned long first, last;
int shift;
- dst += (n-1)/bits;
- src += (n-1)/bits;
- if ((n-1) % bits) {
- dst_idx += (n-1) % bits;
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= bits - 1;
- src_idx += (n-1) % bits;
- src += src_idx >> (ffs(bits) - 1);
- src_idx &= bits - 1;
- }
+#if 0
+ /*
+ * If you suspect bug in this function, compare it with this simple
+ * memmove implementation.
+ */
+ fb_memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
+ (char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
+ return;
+#endif
+
+ dst += (dst_idx + n - 1) / bits;
+ src += (src_idx + n - 1) / bits;
+ dst_idx = (dst_idx + n - 1) % bits;
+ src_idx = (src_idx + n - 1) % bits;
shift = dst_idx-src_idx;
- first = fb_shifted_pixels_mask_long(p, bits - 1 - dst_idx, bswapmask);
- last = ~fb_shifted_pixels_mask_long(p, bits - 1 - ((dst_idx-n) % bits),
- bswapmask);
+ first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
+ last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
if (!shift) {
// Same alignment for source and dest
if ((unsigned long)dst_idx+1 >= n) {
// Single word
- if (last)
- first &= last;
- FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
+ if (first)
+ last &= first;
+ FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
} else {
// Multiple destination words
// Leading bits
- if (first != ~0UL) {
+ if (first) {
FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
dst--;
src--;
@@ -262,7 +272,7 @@ bitcpy_rev(struct fb_info *p, unsigned l
FB_WRITEL(FB_READL(src--), dst--);
// Trailing bits
- if (last)
+ if (last != -1UL)
FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
}
} else {
@@ -270,29 +280,28 @@ bitcpy_rev(struct fb_info *p, unsigned l
unsigned long d0, d1;
int m;
- int const left = -shift & (bits-1);
- int const right = shift & (bits-1);
- bswapmask &= shift;
+ int const left = shift & (bits-1);
+ int const right = -shift & (bits-1);
if ((unsigned long)dst_idx+1 >= n) {
// Single destination word
- if (last)
- first &= last;
+ if (first)
+ last &= first;
d0 = FB_READL(src);
if (shift < 0) {
// Single source word
- d0 <<= left;
+ d0 >>= right;
} else if (1+(unsigned long)src_idx >= n) {
// Single source word
- d0 >>= right;
+ d0 <<= left;
} else {
// 2 source words
d1 = FB_READL(src - 1);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0>>right | d1<<left;
+ d0 = d0 << left | d1 >> right;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
- FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
+ FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
} else {
// Multiple destination words
/** We must always remember the last value read, because in case
@@ -307,12 +316,12 @@ bitcpy_rev(struct fb_info *p, unsigned l
if (shift < 0) {
// Single source word
d1 = d0;
- d0 <<= left;
+ d0 >>= right;
} else {
// 2 source words
d1 = FB_READL(src--);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0>>right | d1<<left;
+ d0 = d0 << left | d1 >> right;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
@@ -325,39 +334,39 @@ bitcpy_rev(struct fb_info *p, unsigned l
n /= bits;
while ((n >= 4) && !bswapmask) {
d1 = FB_READL(src--);
- FB_WRITEL(d0 >> right | d1 << left, dst--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
d0 = d1;
d1 = FB_READL(src--);
- FB_WRITEL(d0 >> right | d1 << left, dst--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
d0 = d1;
d1 = FB_READL(src--);
- FB_WRITEL(d0 >> right | d1 << left, dst--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
d0 = d1;
d1 = FB_READL(src--);
- FB_WRITEL(d0 >> right | d1 << left, dst--);
+ FB_WRITEL(d0 << left | d1 >> right, dst--);
d0 = d1;
n -= 4;
}
while (n--) {
d1 = FB_READL(src--);
d1 = fb_rev_pixels_in_long(d1, bswapmask);
- d0 = d0 >> right | d1 << left;
+ d0 = d0 << left | d1 >> right;
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(d0, dst--);
d0 = d1;
}
// Trailing bits
- if (last) {
- if (m <= left) {
+ if (m) {
+ if (m <= bits - left) {
// Single source word
- d0 >>= right;
+ d0 <<= left;
} else {
// 2 source words
d1 = FB_READL(src);
d1 = fb_rev_pixels_in_long(d1,
bswapmask);
- d0 = d0>>right | d1<<left;
+ d0 = d0 << left | d1 >> right;
}
d0 = fb_rev_pixels_in_long(d0, bswapmask);
FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
@@ -371,9 +380,9 @@ void cfb_copyarea(struct fb_info *p, con
u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
u32 height = area->height, width = area->width;
unsigned long const bits_per_line = p->fix.line_length*8u;
- unsigned long __iomem *dst = NULL, *src = NULL;
+ unsigned long __iomem *base = NULL;
int bits = BITS_PER_LONG, bytes = bits >> 3;
- int dst_idx = 0, src_idx = 0, rev_copy = 0;
+ unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
u32 bswapmask = fb_compute_bswapmask(p);
if (p->state != FBINFO_STATE_RUNNING)
@@ -389,7 +398,7 @@ void cfb_copyarea(struct fb_info *p, con
// split the base of the framebuffer into a long-aligned address and the
// index of the first bit
- dst = src = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
+ base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
// add offset of source and target area
dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
@@ -402,20 +411,14 @@ void cfb_copyarea(struct fb_info *p, con
while (height--) {
dst_idx -= bits_per_line;
src_idx -= bits_per_line;
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= (bytes - 1);
- src += src_idx >> (ffs(bits) - 1);
- src_idx &= (bytes - 1);
- bitcpy_rev(p, dst, dst_idx, src, src_idx, bits,
+ bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
+ base + (src_idx / bits), src_idx % bits, bits,
width*p->var.bits_per_pixel, bswapmask);
}
} else {
while (height--) {
- dst += dst_idx >> (ffs(bits) - 1);
- dst_idx &= (bytes - 1);
- src += src_idx >> (ffs(bits) - 1);
- src_idx &= (bytes - 1);
- bitcpy(p, dst, dst_idx, src, src_idx, bits,
+ bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
+ base + (src_idx / bits), src_idx % bits, bits,
width*p->var.bits_per_pixel, bswapmask);
dst_idx += bits_per_line;
src_idx += bits_per_line;
^ permalink raw reply
* [PATCH 4/10] atyfb: set FBINFO_READS_FAST
From: Mikulas Patocka @ 2014-01-23 19:40 UTC (permalink / raw)
To: linux-fbdev
In-Reply-To: <CA+55aFxPfHmpcMH-bmRcTPUQeq5=67cKVmra6+SvT26ybvQaug@mail.gmail.com>
Set FBINFO_READS_FAST so that the console code uses scrolling instead of
rewriting. This improves scrolling speed.
A time to do ls -la /usr/bin:
original patched
32bpp 4.9 3.6
24bpp 4.9 2.9
16bpp 4.9 2.1
8bpp 4.9 1.7
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
---
drivers/video/aty/atyfb_base.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Index: linux-3.13-rc1/drivers/video/aty/atyfb_base.c
=================================--- linux-3.13-rc1.orig/drivers/video/aty/atyfb_base.c 2013-11-27 00:25:42.000000000 +0100
+++ linux-3.13-rc1/drivers/video/aty/atyfb_base.c 2013-11-27 00:25:55.000000000 +0100
@@ -2653,7 +2653,8 @@ static int aty_init(struct fb_info *info
FBINFO_HWACCEL_IMAGEBLIT |
FBINFO_HWACCEL_FILLRECT |
FBINFO_HWACCEL_COPYAREA |
- FBINFO_HWACCEL_YPAN;
+ FBINFO_HWACCEL_YPAN |
+ FBINFO_READS_FAST;
#ifdef CONFIG_PMAC_BACKLIGHT
if (M64_HAS(G3_PB_1_1) && of_machine_is_compatible("PowerBook1,1")) {
^ permalink raw reply
* [PATCH 5/10] atyfb: remove resolution limit 1600
From: Mikulas Patocka @ 2014-01-23 19:40 UTC (permalink / raw)
To: linux-fbdev
The card works fine in 1980x1080 resolution. Therefore, there is no need
to limit the resolution to 1600 pixels.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
---
drivers/video/aty/atyfb_base.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
Index: linux-3.11-rc7-fast/drivers/video/aty/atyfb_base.c
=================================--- linux-3.11-rc7-fast.orig/drivers/video/aty/atyfb_base.c 2013-09-02 18:50:52.000000000 +0200
+++ linux-3.11-rc7-fast/drivers/video/aty/atyfb_base.c 2013-09-02 18:50:58.000000000 +0200
@@ -862,8 +862,8 @@ static int aty_var_to_crtc(const struct
h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
- if ((xres > 1600) || (yres > 1200)) {
- FAIL("MACH64 chips are designed for max 1600x1200\n"
+ if ((xres > 1920) || (yres > 1200)) {
+ FAIL("MACH64 chips are designed for max 1920x1200\n"
"select another resolution.");
}
h_sync_strt = h_disp + var->right_margin;
^ permalink raw reply
* [PATCH 6/10] mach64: use unaligned access
From: Mikulas Patocka @ 2014-01-23 19:41 UTC (permalink / raw)
To: linux-fbdev
This patch fixes mach64 to use unaligned access to the font bitmap.
This fixes unaligned access warning on sparc64 when 14x8 font is loaded.
On x86(64), unaligned access is handled in hardware, so both functions
le32_to_cpup and get_unaligned_le32 perform the same operation.
On RISC machines, unaligned access is not handled in hardware, so we
better use get_unaligned_le32 to avoid the unaligned trap and warning.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: stable@vger.kernel.org
---
drivers/video/aty/mach64_accel.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Index: linux-3.4.3-fast/drivers/video/aty/mach64_accel.c
=================================--- linux-3.4.3-fast.orig/drivers/video/aty/mach64_accel.c 2012-07-02 00:10:56.000000000 +0200
+++ linux-3.4.3-fast/drivers/video/aty/mach64_accel.c 2012-07-02 00:13:29.000000000 +0200
@@ -4,6 +4,7 @@
*/
#include <linux/delay.h>
+#include <asm/unaligned.h>
#include <linux/fb.h>
#include <video/mach64.h>
#include "atyfb.h"
@@ -419,7 +420,7 @@ void atyfb_imageblit(struct fb_info *inf
u32 *pbitmap, dwords = (src_bytes + 3) / 4;
for (pbitmap = (u32*)(image->data); dwords; dwords--, pbitmap++) {
wait_for_fifo(1, par);
- aty_st_le32(HOST_DATA0, le32_to_cpup(pbitmap), par);
+ aty_st_le32(HOST_DATA0, get_unaligned_le32(pbitmap), par);
}
}
^ permalink raw reply
* [PATCH 7/10] mach64: fix cursor when character width is not a multiple of 8 pixels
From: Mikulas Patocka @ 2014-01-23 19:41 UTC (permalink / raw)
To: linux-fbdev
This patch fixes the hardware cursor on mach64 when font width is not a
multiple of 8 pixels.
If you load such a font, the cursor is expanded to the next 8-byte
boundary and a part of the next character after the cursor is not
visible.
For example, when you load a font with 12-pixel width, the cursor width
is 16 pixels and when the cursor is displayed, 4 pixels of the next
character are not visible.
The reason is this: atyfb_cursor is called with proper parameters to
load an image that is 12-pixel wide. However, the number is aligned on
the next 8-pixel boundary on the line
"unsigned int width = (cursor->image.width + 7) >> 3;" and the whole
function acts as it is was loading a 16-pixel image.
This patch fixes it so that the value written to the framebuffer is
padded with 0xaaaa (the transparent pattern) when the image size it not
a multiple of 8 pixels. The transparent pattern causes that the cursor
will not interfere with the next character.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: stable@vger.kernel.org
---
drivers/video/aty/mach64_cursor.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
Index: linux-3.4.3-fast/drivers/video/aty/mach64_cursor.c
=================================--- linux-3.4.3-fast.orig/drivers/video/aty/mach64_cursor.c 2012-07-02 01:15:38.000000000 +0200
+++ linux-3.4.3-fast/drivers/video/aty/mach64_cursor.c 2012-07-02 01:54:10.000000000 +0200
@@ -5,6 +5,7 @@
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/string.h>
+#include "../fb_draw.h"
#include <asm/io.h>
@@ -157,24 +158,33 @@ static int atyfb_cursor(struct fb_info *
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
+ u16 l = 0xaaaa;
b = *src++;
m = *msk++;
switch (cursor->rop) {
case ROP_XOR:
// Upper 4 bits of mask data
- fb_writeb(cursor_bits_lookup[(b ^ m) >> 4], dst++);
+ l = cursor_bits_lookup[(b ^ m) >> 4] |
// Lower 4 bits of mask
- fb_writeb(cursor_bits_lookup[(b ^ m) & 0x0f],
- dst++);
+ (cursor_bits_lookup[(b ^ m) & 0x0f] << 8);
break;
case ROP_COPY:
// Upper 4 bits of mask data
- fb_writeb(cursor_bits_lookup[(b & m) >> 4], dst++);
+ l = cursor_bits_lookup[(b & m) >> 4] |
// Lower 4 bits of mask
- fb_writeb(cursor_bits_lookup[(b & m) & 0x0f],
- dst++);
+ (cursor_bits_lookup[(b & m) & 0x0f] << 8);
break;
}
+ /*
+ * If cursor size is not a multiple of 8 characters
+ * we must pad it with transparent pattern (0xaaaa).
+ */
+ if ((j + 1) * 8 > cursor->image.width) {
+ l = comp(l, 0xaaaa,
+ (1 << ((cursor->image.width & 7) * 2)) - 1);
+ }
+ fb_writeb(l & 0xff, dst++);
+ fb_writeb(l >> 8, dst++);
}
dst += offset;
}
^ permalink raw reply
* [PATCH 8/10] tgafb: fix mode setting with fbset
From: Mikulas Patocka @ 2014-01-23 19:42 UTC (permalink / raw)
To: linux-fbdev
Mode setting in the TGA driver is broken for these reasons:
- info->fix.line_length is set just once in tgafb_init_fix function. If
we change videomode, info->fix.line_length is not recalculated - so
the video mode is changed but the screen is corrupted because of wrong
info->fix.line_length.
- info->fix.smem_len is set in tgafb_init_fix to the size of the default
video mode (640x480). If we set a higher resolution,
info->fix.smem_len is smaller than the current screen size, preventing
the userspace program from mapping the framebuffer.
This patch fixes it:
- info->fix.line_length initialization is moved to tgafb_set_par so that
it is recalculated with each mode change.
- info->fix.smem_len is set to a fixed value representing the real
amount of video ram (the values are taken from xfree86 driver).
- add a check to tgafb_check_var to prevent us from setting a videomode
that doesn't fit into videoram.
- in tgafb_register, tgafb_init_fix is moved upwards, to be called
before fb_find_mode (because fb_find_mode already needs the videoram
size set in tgafb_init_fix).
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: stable@vga.kernel.org
---
drivers/video/tgafb.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
Index: linux-3.8-rc3-fast/drivers/video/tgafb.c
=================================--- linux-3.8-rc3-fast.orig/drivers/video/tgafb.c 2013-01-10 23:42:12.000000000 +0100
+++ linux-3.8-rc3-fast/drivers/video/tgafb.c 2013-01-10 23:43:16.000000000 +0100
@@ -188,6 +188,8 @@ tgafb_check_var(struct fb_var_screeninfo
if (var->xres_virtual != var->xres || var->yres_virtual != var->yres)
return -EINVAL;
+ if (var->xres * var->yres * (var->bits_per_pixel >> 3) > info->fix.smem_len)
+ return -EINVAL;
if (var->nonstd)
return -EINVAL;
if (1000000000 / var->pixclock > TGA_PLL_MAX_FREQ)
@@ -268,6 +270,7 @@ tgafb_set_par(struct fb_info *info)
par->yres = info->var.yres;
par->pll_freq = pll_freq = 1000000000 / info->var.pixclock;
par->bits_per_pixel = info->var.bits_per_pixel;
+ info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
tga_type = par->tga_type;
@@ -1476,6 +1479,7 @@ tgafb_init_fix(struct fb_info *info)
int tga_bus_tc = TGA_BUS_TC(par->dev);
u8 tga_type = par->tga_type;
const char *tga_type_name = NULL;
+ unsigned memory_size;
switch (tga_type) {
case TGA_TYPE_8PLANE:
@@ -1483,21 +1487,25 @@ tgafb_init_fix(struct fb_info *info)
tga_type_name = "Digital ZLXp-E1";
if (tga_bus_tc)
tga_type_name = "Digital ZLX-E1";
+ memory_size = 2097152;
break;
case TGA_TYPE_24PLANE:
if (tga_bus_pci)
tga_type_name = "Digital ZLXp-E2";
if (tga_bus_tc)
tga_type_name = "Digital ZLX-E2";
+ memory_size = 8388608;
break;
case TGA_TYPE_24PLUSZ:
if (tga_bus_pci)
tga_type_name = "Digital ZLXp-E3";
if (tga_bus_tc)
tga_type_name = "Digital ZLX-E3";
+ memory_size = 16777216;
break;
default:
tga_type_name = "Unknown";
+ memory_size = 16777216;
break;
}
@@ -1509,9 +1517,8 @@ tgafb_init_fix(struct fb_info *info)
? FB_VISUAL_PSEUDOCOLOR
: FB_VISUAL_DIRECTCOLOR);
- info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
info->fix.smem_start = (size_t) par->tga_fb_base;
- info->fix.smem_len = info->fix.line_length * par->yres;
+ info->fix.smem_len = memory_size;
info->fix.mmio_start = (size_t) par->tga_regs_base;
info->fix.mmio_len = 512;
@@ -1635,6 +1642,9 @@ static int tgafb_register(struct device
modedb_tga = &modedb_tc;
modedbsize_tga = 1;
}
+
+ tgafb_init_fix(info);
+
ret = fb_find_mode(&info->var, info,
mode_option ? mode_option : mode_option_tga,
modedb_tga, modedbsize_tga, NULL,
@@ -1652,7 +1662,6 @@ static int tgafb_register(struct device
}
tgafb_set_par(info);
- tgafb_init_fix(info);
if (register_framebuffer(info) < 0) {
printk(KERN_ERR "tgafb: Could not register framebuffer\n");
^ permalink raw reply
* [PATCH 9/10] tgafb: fix data copying
From: Mikulas Patocka @ 2014-01-23 19:43 UTC (permalink / raw)
To: linux-fbdev
The functions for data copying copyarea_foreward_8bpp and
copyarea_backward_8bpp are buggy, they produce screen corruption.
This patch fixes the functions and moves the logic to one function
"copyarea_8bpp". For simplicity, the function only handles copying that
is aligned on 8 pixes. If we copy an unaligned area, generic function
cfb_copyarea is used.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Cc: stable@vger.kernel.org
---
drivers/video/tgafb.c | 264 +++++++++-----------------------------------------
1 file changed, 51 insertions(+), 213 deletions(-)
Index: linux-3.8-rc3-fast/drivers/video/tgafb.c
=================================--- linux-3.8-rc3-fast.orig/drivers/video/tgafb.c 2013-01-10 23:43:16.000000000 +0100
+++ linux-3.8-rc3-fast/drivers/video/tgafb.c 2013-01-10 23:43:44.000000000 +0100
@@ -1145,222 +1145,57 @@ copyarea_line_32bpp(struct fb_info *info
__raw_writel(TGA_MODE_SBM_24BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
}
-/* The general case of forward copy in 8bpp mode. */
+/* The (almost) general case of backward copy in 8bpp mode. */
static inline void
-copyarea_foreward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
- u32 height, u32 width, u32 line_length)
+copyarea_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
+ u32 height, u32 width, u32 line_length,
+ const struct fb_copyarea *area)
{
struct tga_par *par = (struct tga_par *) info->par;
- unsigned long i, copied, left;
- unsigned long dpos, spos, dalign, salign, yincr;
- u32 smask_first, dmask_first, dmask_last;
- int pixel_shift, need_prime, need_second;
- unsigned long n64, n32, xincr_first;
+ unsigned i, yincr;
+ int depos, sepos, backward, last_step, step;
+ u32 mask_last;
+ unsigned n32;
void __iomem *tga_regs;
void __iomem *tga_fb;
- yincr = line_length;
- if (dy > sy) {
- dy += height - 1;
- sy += height - 1;
- yincr = -yincr;
- }
-
- /* Compute the offsets and alignments in the frame buffer.
- More than anything else, these control how we do copies. */
- dpos = dy * line_length + dx;
- spos = sy * line_length + sx;
- dalign = dpos & 7;
- salign = spos & 7;
- dpos &= -8;
- spos &= -8;
-
- /* Compute the value for the PIXELSHIFT register. This controls
- both non-co-aligned source and destination and copy direction. */
- if (dalign >= salign)
- pixel_shift = dalign - salign;
- else
- pixel_shift = 8 - (salign - dalign);
-
- /* Figure out if we need an additional priming step for the
- residue register. */
- need_prime = (salign > dalign);
- if (need_prime)
- dpos -= 8;
-
- /* Begin by copying the leading unaligned destination. Copy enough
- to make the next destination address 32-byte aligned. */
- copied = 32 - (dalign + (dpos & 31));
- if (copied = 32)
- copied = 0;
- xincr_first = (copied + 7) & -8;
- smask_first = dmask_first = (1ul << copied) - 1;
- smask_first <<= salign;
- dmask_first <<= dalign + need_prime*8;
- if (need_prime && copied > 24)
- copied -= 8;
- left = width - copied;
-
- /* Care for small copies. */
- if (copied > width) {
- u32 t;
- t = (1ul << width) - 1;
- t <<= dalign + need_prime*8;
- dmask_first &= t;
- left = 0;
- }
-
- /* Attempt to use 64-byte copies. This is only possible if the
- source and destination are co-aligned at 64 bytes. */
- n64 = need_second = 0;
- if ((dpos & 63) = (spos & 63)
- && (height = 1 || line_length % 64 = 0)) {
- /* We may need a 32-byte copy to ensure 64 byte alignment. */
- need_second = (dpos + xincr_first) & 63;
- if ((need_second & 32) != need_second)
- printk(KERN_ERR "tgafb: need_second wrong\n");
- if (left >= need_second + 64) {
- left -= need_second;
- n64 = left / 64;
- left %= 64;
- } else
- need_second = 0;
- }
-
- /* Copy trailing full 32-byte sections. This will be the main
- loop if the 64 byte loop can't be used. */
- n32 = left / 32;
- left %= 32;
-
- /* Copy the trailing unaligned destination. */
- dmask_last = (1ul << left) - 1;
-
- tga_regs = par->tga_regs_base;
- tga_fb = par->tga_fb_base;
-
- /* Set up the MODE and PIXELSHIFT registers. */
- __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
- __raw_writel(pixel_shift, tga_regs+TGA_PIXELSHIFT_REG);
- wmb();
-
- for (i = 0; i < height; ++i) {
- unsigned long j;
- void __iomem *sfb;
- void __iomem *dfb;
-
- sfb = tga_fb + spos;
- dfb = tga_fb + dpos;
- if (dmask_first) {
- __raw_writel(smask_first, sfb);
- wmb();
- __raw_writel(dmask_first, dfb);
- wmb();
- sfb += xincr_first;
- dfb += xincr_first;
- }
-
- if (need_second) {
- __raw_writel(0xffffffff, sfb);
- wmb();
- __raw_writel(0xffffffff, dfb);
- wmb();
- sfb += 32;
- dfb += 32;
- }
-
- if (n64 && (((unsigned long)sfb | (unsigned long)dfb) & 63))
- printk(KERN_ERR
- "tgafb: misaligned copy64 (s:%p, d:%p)\n",
- sfb, dfb);
-
- for (j = 0; j < n64; ++j) {
- __raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
- wmb();
- __raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
- wmb();
- sfb += 64;
- dfb += 64;
- }
-
- for (j = 0; j < n32; ++j) {
- __raw_writel(0xffffffff, sfb);
- wmb();
- __raw_writel(0xffffffff, dfb);
- wmb();
- sfb += 32;
- dfb += 32;
- }
-
- if (dmask_last) {
- __raw_writel(0xffffffff, sfb);
- wmb();
- __raw_writel(dmask_last, dfb);
- wmb();
- }
-
- spos += yincr;
- dpos += yincr;
+ /* Do acceleration only if we are aligned on 8 pixels */
+ if ((dx | sx | width) & 7) {
+ cfb_copyarea(info, area);
+ return;
}
- /* Reset the MODE register to normal. */
- __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
-}
-
-/* The (almost) general case of backward copy in 8bpp mode. */
-static inline void
-copyarea_backward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
- u32 height, u32 width, u32 line_length,
- const struct fb_copyarea *area)
-{
- struct tga_par *par = (struct tga_par *) info->par;
- unsigned long i, left, yincr;
- unsigned long depos, sepos, dealign, sealign;
- u32 mask_first, mask_last;
- unsigned long n32;
- void __iomem *tga_regs;
- void __iomem *tga_fb;
-
yincr = line_length;
if (dy > sy) {
dy += height - 1;
sy += height - 1;
yincr = -yincr;
}
+ backward = dy = sy && dx > sx && dx < sx + width;
/* Compute the offsets and alignments in the frame buffer.
More than anything else, these control how we do copies. */
- depos = dy * line_length + dx + width;
- sepos = sy * line_length + sx + width;
- dealign = depos & 7;
- sealign = sepos & 7;
-
- /* ??? The documentation appears to be incorrect (or very
- misleading) wrt how pixel shifting works in backward copy
- mode, i.e. when PIXELSHIFT is negative. I give up for now.
- Do handle the common case of co-aligned backward copies,
- but frob everything else back on generic code. */
- if (dealign != sealign) {
- cfb_copyarea(info, area);
- return;
- }
-
- /* We begin the copy with the trailing pixels of the
- unaligned destination. */
- mask_first = (1ul << dealign) - 1;
- left = width - dealign;
-
- /* Care for small copies. */
- if (dealign > width) {
- mask_first ^= (1ul << (dealign - width)) - 1;
- left = 0;
- }
+ depos = dy * line_length + dx;
+ sepos = sy * line_length + sx;
+ if (backward)
+ depos += width, sepos += width;
/* Next copy full words at a time. */
- n32 = left / 32;
- left %= 32;
+ n32 = width / 32;
+ last_step = width % 32;
/* Finally copy the unaligned head of the span. */
- mask_last = -1 << (32 - left);
+ mask_last = (1ul << last_step) - 1;
+
+ if (!backward) {
+ step = 32;
+ last_step = 32;
+ } else {
+ step = -32;
+ last_step = -last_step;
+ sepos -= 32;
+ depos -= 32;
+ }
tga_regs = par->tga_regs_base;
tga_fb = par->tga_fb_base;
@@ -1377,25 +1212,33 @@ copyarea_backward_8bpp(struct fb_info *i
sfb = tga_fb + sepos;
dfb = tga_fb + depos;
- if (mask_first) {
- __raw_writel(mask_first, sfb);
- wmb();
- __raw_writel(mask_first, dfb);
- wmb();
- }
- for (j = 0; j < n32; ++j) {
- sfb -= 32;
- dfb -= 32;
+ for (j = 0; j < n32; j++) {
+ if (j < 2 && j + 1 < n32 && !backward &&
+ !(((unsigned long)sfb | (unsigned long)dfb) & 63)) {
+ do {
+ __raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
+ wmb();
+ __raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
+ wmb();
+ sfb += 64;
+ dfb += 64;
+ j += 2;
+ } while (j + 1 < n32);
+ j--;
+ continue;
+ }
__raw_writel(0xffffffff, sfb);
wmb();
__raw_writel(0xffffffff, dfb);
wmb();
+ sfb += step;
+ dfb += step;
}
if (mask_last) {
- sfb -= 32;
- dfb -= 32;
+ sfb += last_step - step;
+ dfb += last_step - step;
__raw_writel(mask_last, sfb);
wmb();
__raw_writel(mask_last, dfb);
@@ -1456,14 +1299,9 @@ tgafb_copyarea(struct fb_info *info, con
else if (bpp = 32)
cfb_copyarea(info, area);
- /* Detect overlapping source and destination that requires
- a backward copy. */
- else if (dy = sy && dx > sx && dx < sx + width)
- copyarea_backward_8bpp(info, dx, dy, sx, sy, height,
- width, line_length, area);
else
- copyarea_foreward_8bpp(info, dx, dy, sx, sy, height,
- width, line_length);
+ copyarea_8bpp(info, dx, dy, sx, sy, height,
+ width, line_length, area);
}
^ permalink raw reply
* [PATCH 10/10] tgafb: avoid restriction on modes with line length not a multiple of 64
From: Mikulas Patocka @ 2014-01-23 19:44 UTC (permalink / raw)
To: linux-fbdev
In tgafb there is a restriction that prevents the user from setting a
videomode with line length not a multiple of 64 bytes (for example,
800x600 is not allowed).
The reason for this restriction it that functions copyarea_line_8bpp and
copyarea_line_32bpp can not handle a line length that is not a multiple
of 64 bytes.
This patch removes this restriction on mode setting and makes sure that
the functions copyarea_line_8bpp and copyarea_line_32bpp are called only
if line length is a multiple of 64 bytes. If we set a mode 800x600,
the functions copyarea_line_8bpp and copyarea_line_32bpp are not used,
generic functions for copying are used instead and it works just fine.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
---
drivers/video/tgafb.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
Index: linux-3.8-rc3-fast/drivers/video/tgafb.c
=================================--- linux-3.8-rc3-fast.orig/drivers/video/tgafb.c 2013-01-10 23:43:44.000000000 +0100
+++ linux-3.8-rc3-fast/drivers/video/tgafb.c 2013-01-10 23:43:49.000000000 +0100
@@ -198,8 +198,8 @@ tgafb_check_var(struct fb_var_screeninfo
return -EINVAL;
/* Some of the acceleration routines assume the line width is
- a multiple of 64 bytes. */
- if (var->xres * (par->tga_type = TGA_TYPE_8PLANE ? 1 : 4) % 64)
+ a multiple of 8 bytes. */
+ if (var->xres * (par->tga_type = TGA_TYPE_8PLANE ? 1 : 4) % 8)
return -EINVAL;
return 0;
@@ -1286,7 +1286,7 @@ tgafb_copyarea(struct fb_info *info, con
bpp = info->var.bits_per_pixel;
/* Detect copies of the entire line. */
- if (width * (bpp >> 3) = line_length) {
+ if (!(line_length & 63) && width * (bpp >> 3) = line_length) {
if (bpp = 8)
copyarea_line_8bpp(info, dy, sy, height, width);
else
^ permalink raw reply
* Re: [PATCH 4/10] atyfb: set FBINFO_READS_FAST
From: Linus Torvalds @ 2014-01-23 19:45 UTC (permalink / raw)
To: Mikulas Patocka
Cc: linux-fbdev@vger.kernel.org, Tomi Valkeinen,
Linux Kernel Mailing List
In-Reply-To: <alpine.LRH.2.02.1401231426470.7971@file01.intranet.prod.int.rdu2.redhat.com>
On Thu, Jan 23, 2014 at 11:31 AM, Mikulas Patocka <mpatocka@redhat.com> wrote:
>
> Both mach64 and matrox have a hardware bitter that is faster than
> rewriting the console - that's why FBINFO_READS_FAST improves performance
> for them.
My point is that I'd expect *anything* that has a hardware blitter to
be faster than rewriting the screen.
FBINFO_READS_FAST is documented to be about "soft-copy" being faster
than re-rendering. Which I take to be about actually doing copying in
*software*.
In particular, updatescrollmode() seems to do this right. It sets
p->scrollmode based on whether there's an accelerated copyarea. But
then SCROLL_PAN/WRAP_MOVE ends up re-testing FBINFO_READS_FAST,
ignoring any hw-accelerated copy-area, and I don't quite see why..
Linus
^ permalink raw reply
* Re: [PATCH 4/10] atyfb: set FBINFO_READS_FAST
From: Mikulas Patocka @ 2014-01-23 20:03 UTC (permalink / raw)
To: Linus Torvalds
Cc: linux-fbdev@vger.kernel.org, Tomi Valkeinen,
Linux Kernel Mailing List
In-Reply-To: <CA+55aFxrZE6oxu5CxFo3mRwGdVcq3jvnn_ChoSJDFgQz45Lq_A@mail.gmail.com>
On Thu, 23 Jan 2014, Linus Torvalds wrote:
> On Thu, Jan 23, 2014 at 11:31 AM, Mikulas Patocka <mpatocka@redhat.com> wrote:
> >
> > Both mach64 and matrox have a hardware bitter that is faster than
> > rewriting the console - that's why FBINFO_READS_FAST improves performance
> > for them.
>
> My point is that I'd expect *anything* that has a hardware blitter to
> be faster than rewriting the screen.
>
> FBINFO_READS_FAST is documented to be about "soft-copy" being faster
> than re-rendering. Which I take to be about actually doing copying in
> *software*.
>
> In particular, updatescrollmode() seems to do this right. It sets
> p->scrollmode based on whether there's an accelerated copyarea. But
> then SCROLL_PAN/WRAP_MOVE ends up re-testing FBINFO_READS_FAST,
> ignoring any hw-accelerated copy-area, and I don't quite see why..
>
> Linus
I think the argument why not to use the blitter was this:
Some hardware have font expansion - you submit monochromatic bitwise image
of the character via writes to some register and the hardware expands it
to full color. Mach64 has this capability, matrox doesn't (maybe it does,
but the driver doesn't use it).
If you use font expansion, you actually generate less load on the videoram
than using the blitter (the expander only writes to the videoram; the
blitter reads data from the videoram and stores them back).
But the benchmarks that I performed show that the blitter is faster than
the font expander. Maybe on a different computer that has better
bufferring in the PCI chipset the font expander could be faster - who
knows?
Mikulas
^ permalink raw reply
* Re: [PATCH v2] backlight: turn backlight on/off when necessary
From: Jingoo Han @ 2014-01-24 2:25 UTC (permalink / raw)
To: 'Liu Ying'
Cc: 'Jani Nikula', linux-fbdev, tomi.valkeinen, plagnioj,
linux-kernel, dri-devel, 'Jingoo Han'
In-Reply-To: <52E0E093.1070803@freescale.com>
On Thursday, January 23, 2014 6:28 PM, Liu Ying wrote:
> On 01/23/2014 01:44 PM, Jingoo Han wrote:
> > On Wednesday, January 22, 2014 6:36 PM, Jani Nikula wrote:
> >> On Mon, 20 Jan 2014, Liu Ying <Ying.Liu@freescale.com> wrote:
> >>> We don't have to turn backlight on/off everytime a blanking
> >>> or unblanking event comes because the backlight status may
> >>> have already been what we want. Another thought is that one
> >>> backlight device may be shared by multiple framebuffers. We
> >>> don't hope blanking one of the framebuffers may turn the
> >>> backlight off for all the other framebuffers, since they are
> >>> likely being active to display something. This patch adds
> >>> some logics to record each framebuffer's backlight usage to
> >>> determine the backlight device use count and whether the
> >>> backlight should be turned on or off. To be more specific,
> >>> only one unblank operation on a certain blanked framebuffer
> >>> may increase the backlight device's use count by one, while
> >>> one blank operation on a certain unblanked framebuffer may
> >>> decrease the use count by one, because the userspace is
> >>> likely to unblank a unblanked framebuffer or blank a blanked
> >>> framebuffer.
> >>>
> >>> Signed-off-by: Liu Ying <Ying.Liu@freescale.com>
> >>> ---
> >>> v1 can be found at https://lkml.org/lkml/2013/5/30/139
> >>>
> >>> v1->v2:
> >>> * Make the commit message be more specific about the condition
> >>> in which backlight device use count can be increased/decreased.
> >>> * Correct the setting for bd->props.fb_blank.
> >>>
> >>> drivers/video/backlight/backlight.c | 28 +++++++++++++++++++++-------
> >>> include/linux/backlight.h | 6 ++++++
> >>> 2 files changed, 27 insertions(+), 7 deletions(-)
> >>>
> >
> > [.....]
> >>
> >> Anything backlight worries me a little, and there are actually three
> >> changes bundled into one patch here:
> >>
> >> 1. Changing bd->props.state and bd->props.fb_blank only when use_count
> >> changes from 0->1 or 1->0.
> >>
> >> 2. Calling backlight_update_status() only with the above change, and not
> >> on all notifier callbacks.
> >>
> >> 3. Setting bd->props.fb_blank always to either FB_BLANK_UNBLANK or
> >> FB_BLANK_POWERDOWN instead of *(int *)evdata->data.
>
> Since I have already post v3(https://lkml.org/lkml/2014/1/22/126)
> to change the setting for bd->props.fb_blank, the idea of the 3rd point
> is not very appropriate any more.
OK, I see.
>
> >>
> >> The rationale in the commit message seems plausible, and AFAICT the code
> >> does what it says on the box, so for that (and for that alone) you can
> >> have my
> >>
> >> Reviewed-by: Jani Nikula <jani.nikula@intel.com>
> >>
> >> *BUT* it would be laborous to figure out whether this change in
> >> behaviour might regress some drivers. I'm just punting on that. And that
> >> brings us back to the three changes above - in a bisect POV it might be
> >> helpful to split the patch up. Up to the maintainers.
> >
> > I agree with Jani Nikula's opinion.
> > Please split this patch into three patches as above mentioned.
> >
>
> I am open to split the patch up.
> However, IMHO, this patch is somewhat self-contained.
> For example, if we try to create 2 patches for the 1st point and
> the 2nd point Jani mentioned, one patch would invent the use_count
> and call backlight_update_status() on all notifier callbacks(just
> ignore the use_count).
> Do you think this is a good patch?
The calling backlight_update_status() regardless the use_count
Will make the critical side effect? I don't think so.
Also, these two patches will be merged at the same time.
Please, split the patch into two patches. It would be clearer.
One more thing, please keep the indent using "Enter", when
sending your reply mail.
Best regards,
Jingoo Han
^ permalink raw reply
* [PATCH v4 0/2] backlight: update bl status and some bd properties when necessary
From: Liu Ying @ 2014-01-24 6:04 UTC (permalink / raw)
To: jg1.han; +Cc: linux-fbdev, linux-kernel, tomi.valkeinen, dri-devel, plagnioj
We don't have to turn backlight on/off every time a blanking
or unblanking event comes because the backlight status may
have already been what we want. Another thought is that one
backlight device may be shared by multiple framebuffers. We
don't hope blanking one of the framebuffers may turn the
backlight off for all the other framebuffers, since they are
likely being active to display something. This patch set adds
some logics to record each framebuffer's backlight usage to
determine the backlight device use count and whether the
backlight status should be updated or not.
Liu Ying (2):
backlight: update bd state & fb_blank properties when necessary
backlight: update backlight status when necessary
v3->v4:
* Split v3 into 2 patches to separate the change for updating the
backlight device properties and backlight status, according to
the comments from Jani Nikula and Jingoo Han.
v2->v3:
* Set fb_blank(*(int *)evdata->data) to bd->props.fb_blank
when we turn off a blacklight.
* Correct some trivial typos in the commit message.
v1->v2:
* Make the commit message be more specific about the condition
in which backlight device use count can be increased/decreased.
* Correct the setting for bd->props.fb_blank.
drivers/video/backlight/backlight.c | 28 +++++++++++++++++++++-------
include/linux/backlight.h | 6 ++++++
2 files changed, 27 insertions(+), 7 deletions(-)
--
1.7.9.5
^ permalink raw reply
* [PATCH v4 1/2] backlight: update bd state & fb_blank properties when necessary
From: Liu Ying @ 2014-01-24 6:04 UTC (permalink / raw)
To: jg1.han; +Cc: linux-fbdev, linux-kernel, tomi.valkeinen, dri-devel, plagnioj
In-Reply-To: <1390543476-13499-1-git-send-email-Ying.Liu@freescale.com>
We don't have to update the state and fb_blank properties of
a backlight device every time a blanking or unblanking event
comes because they may have already been what we want. Another
thought is that one backlight device may be shared by multiple
framebuffers. The backlight driver should take the backlight
device as a resource shared by all the associated framebuffers.
This patch adds some logics to record each framebuffer's backlight
usage to determine the backlight device use count and whether
the two properties should be updated or not. To be more specific,
only one unblank operation on a certain blanked framebuffer may
increase the backlight device's use count by one, while one blank
operation on a certain unblanked framebuffer may decrease the use
count by one, because the userspace is likely to unblank an
unblanked framebuffer or blank a blanked framebuffer.
Signed-off-by: Liu Ying <Ying.Liu@freescale.com>
---
drivers/video/backlight/backlight.c | 23 ++++++++++++++++++-----
include/linux/backlight.h | 6 ++++++
2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index 5d05555..efc9a2d 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -41,6 +41,8 @@ static int fb_notifier_callback(struct notifier_block *self,
{
struct backlight_device *bd;
struct fb_event *evdata = data;
+ int node = evdata->info->node;
+ int fb_blank = 0;
/* If we aren't interested in this event, skip it immediately ... */
if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK)
@@ -51,11 +53,22 @@ static int fb_notifier_callback(struct notifier_block *self,
if (bd->ops)
if (!bd->ops->check_fb ||
bd->ops->check_fb(bd, evdata->info)) {
- bd->props.fb_blank = *(int *)evdata->data;
- if (bd->props.fb_blank = FB_BLANK_UNBLANK)
- bd->props.state &= ~BL_CORE_FBBLANK;
- else
- bd->props.state |= BL_CORE_FBBLANK;
+ fb_blank = *(int *)evdata->data;
+ if (fb_blank = FB_BLANK_UNBLANK &&
+ !bd->fb_bl_on[node]) {
+ bd->fb_bl_on[node] = true;
+ if (!bd->use_count++) {
+ bd->props.state &= ~BL_CORE_FBBLANK;
+ bd->props.fb_blank = FB_BLANK_UNBLANK;
+ }
+ } else if (fb_blank != FB_BLANK_UNBLANK &&
+ bd->fb_bl_on[node]) {
+ bd->fb_bl_on[node] = false;
+ if (!(--bd->use_count)) {
+ bd->props.state |= BL_CORE_FBBLANK;
+ bd->props.fb_blank = fb_blank;
+ }
+ }
backlight_update_status(bd);
}
mutex_unlock(&bd->ops_lock);
diff --git a/include/linux/backlight.h b/include/linux/backlight.h
index 5f9cd96..7264742 100644
--- a/include/linux/backlight.h
+++ b/include/linux/backlight.h
@@ -9,6 +9,7 @@
#define _LINUX_BACKLIGHT_H
#include <linux/device.h>
+#include <linux/fb.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
@@ -104,6 +105,11 @@ struct backlight_device {
struct list_head entry;
struct device dev;
+
+ /* Multiple framebuffers may share one backlight device */
+ bool fb_bl_on[FB_MAX];
+
+ int use_count;
};
static inline void backlight_update_status(struct backlight_device *bd)
--
1.7.9.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox