Den 04.08.2016 16:03, skrev Noralf Trønnes: > 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 > conversion-helper. > > The driver was originally written by David Herrmann in 2014. > My main contribution is to make use of drm_simple_kms_helper and > rework the probe path to avoid use of the deprecated drm_platform_init() > and drm_driver.{load,unload}(). > > Cc: dh.herrmann at gmail.com > Signed-off-by: Noralf Trønnes <noralf at tronnes.org> > ---
[snip] > diff --git a/drivers/gpu/drm/simpledrm/Kconfig > b/drivers/gpu/drm/simpledrm/Kconfig > new file mode 100644 > index 0000000..f45b25d > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/Kconfig > @@ -0,0 +1,19 @@ > +config DRM_SIMPLEDRM > + tristate "Simple firmware framebuffer DRM driver" FB_SIMPLE is bool, should we do the same here? Another thing is that simplefb.c is registred at fs_initcall(). Should we do that? Here's the commit message introducing it: simplefb: Change simplefb_init from module_init to fs_initcall One of the reasons for having the simplefb nodes in /chosen, and doing explicit enumeration of the nodes there, is too allow enumerating them sooner, so that we get a console earlier on. Doing this earlier then fs_initcall is not useful, since the fb only turns into a console when fbcon intializes, which is a fs_initcall too. Signed-off-by: Hans de Goede <hdegoede at redhat.com> Noralf. > + depends on DRM > + select DRM_KMS_HELPER > + 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..f6a62dc > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/Makefile > @@ -0,0 +1,4 @@ > +simpledrm-y := simpledrm_drv.o simpledrm_kms.o simpledrm_gem.o \ > + simpledrm_damage.o > + > +obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o > diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h > b/drivers/gpu/drm/simpledrm/simpledrm.h > new file mode 100644 > index 0000000..e841655 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm.h > @@ -0,0 +1,87 @@ > +/* > + * SimpleDRM firmware framebuffer driver > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann at 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> > +#include <drm/drm_gem.h> > +#include <drm/drm_simple_kms_helper.h> > + > +struct sdrm_device; > +struct sdrm_gem_object; > +struct sdrm_framebuffer; > + > +struct sdrm_device { > + struct drm_device *ddev; > + struct drm_simple_display_pipe pipe; > + struct drm_connector conn; > + > + /* 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; > +}; > + > +int sdrm_drm_modeset_init(struct sdrm_device *sdrm); > +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma); > + > +int sdrm_dirty(struct drm_framebuffer *fb, > + struct drm_file *file, > + unsigned int flags, unsigned int color, > + struct drm_clip_rect *clips, > + unsigned int num_clips); > +int sdrm_dirty_all_locked(struct sdrm_device *sdrm); > +int sdrm_dirty_all_unlocked(struct sdrm_device *sdrm); > + > +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); > + > +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); > + > +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..e87dac7 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c > @@ -0,0 +1,314 @@ > +/* > + * SimpleDRM firmware framebuffer driver > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann at 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 >>= 11; > + g >>= 10; > + b >>= 11; > + put_unaligned((u16)((r << 11) | (g << 5) | b), (u16 *)dst); > + break; > + case DRM_FORMAT_XRGB1555: > + case DRM_FORMAT_ARGB1555: > + r >>= 11; > + g >>= 11; > + b >>= 11; > + put_unaligned((u16)((r << 10) | (g << 5) | b), (u16 *)dst); > + break; > + case DRM_FORMAT_RGB888: > + r >>= 8; > + g >>= 8; > + b >>= 8; > +#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 >>= 8; > + g >>= 8; > + b >>= 8; > + put_unaligned((u32)((r << 16) | (g << 8) | b), (u32 *)dst); > + break; > + case DRM_FORMAT_ABGR8888: > + r >>= 8; > + g >>= 8; > + b >>= 8; > + put_unaligned((u32)((b << 16) | (g << 8) | r), (u32 *)dst); > + break; > + case DRM_FORMAT_XRGB2101010: > + case DRM_FORMAT_ARGB2101010: > + r >>= 4; > + g >>= 4; > + b >>= 4; > + 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) >> 8, > + (val & 0x0000ff00U), > + (val & 0x000000ffU) << 8); > + } > + > + 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), > + (val & 0x07e0) << 5, > + (val & 0x001f) << 11); > + } > + > + 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->pipe.plane.fb == fb) { > + xoff = sdrm->pipe.crtc.x; > + yoff = sdrm->pipe.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, > + 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, > + DMA_FROM_DEVICE); > +} > + > +int sdrm_dirty(struct drm_framebuffer *fb, > + struct drm_file *file, > + unsigned int flags, unsigned int color, > + struct drm_clip_rect *clips, > + unsigned int num_clips) > +{ > + struct sdrm_framebuffer *sfb = to_sdrm_fb(fb); > + struct drm_device *ddev = fb->dev; > + struct sdrm_device *sdrm = ddev->dev_private; > + struct drm_clip_rect full_clip; > + unsigned int i; > + int r; > + > + if (!clips || !num_clips) { > + full_clip.x1 = 0; > + full_clip.x2 = fb->width; > + full_clip.y1 = 0; > + full_clip.y2 = fb->height; > + clips = &full_clip; > + num_clips = 1; > + } > + > + drm_modeset_lock_all(ddev); > + > + if (sdrm->pipe.plane.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->pipe.plane.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_drv.c > b/drivers/gpu/drm/simpledrm/simpledrm_drv.c > new file mode 100644 > index 0000000..26b4ca9 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c > @@ -0,0 +1,295 @@ > +/* > + * SimpleDRM firmware framebuffer driver > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann at 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_GEM | DRIVER_MODESET | DRIVER_PRIME | > + DRIVER_ATOMIC, > + .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; > + } > + > + DRM_DEBUG_KMS("format: %s\n", drm_get_format_name(sdrm->fb_format)); > + > + 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) > +{ > + struct sdrm_device *sdrm; > + struct drm_device *ddev; > + int ret; > + > + ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev); > + if (!ddev) > + return -ENOMEM; > + > + sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL); > + if (!sdrm) > + goto err_free; > + > + ddev->platformdev = pdev; > + ddev->dev_private = sdrm; > + sdrm->ddev = ddev; > + > + ret = sdrm_pdev_init(sdrm); > + if (ret) > + goto err_free; > + > + ret = sdrm_drm_modeset_init(sdrm); > + if (ret) > + goto err_destroy; > + > + ret = drm_dev_register(ddev, 0); > + if (ret) > + goto err_cleanup; > + > + platform_set_drvdata(pdev, ddev); > + > + DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name, > + ddev->primary->index); > + > + return 0; > + > +err_cleanup: > + drm_mode_config_cleanup(ddev); > +err_destroy: > + sdrm_pdev_destroy(sdrm); > +err_free: > + drm_dev_unref(ddev); > + kfree(sdrm); > + > + return ret; > +} > + > +static int sdrm_simplefb_remove(struct platform_device *pdev) > +{ > + struct drm_device *ddev = platform_get_drvdata(pdev); > + struct sdrm_device *sdrm = ddev->dev_private; > + > + drm_dev_unregister(ddev); > + drm_mode_config_cleanup(ddev); > + > + /* protect fb_map removal against sdrm_blit() */ > + drm_modeset_lock_all(ddev); > + sdrm_pdev_destroy(sdrm); > + drm_modeset_unlock_all(ddev); > + > + drm_dev_unref(ddev); > + kfree(sdrm); > + > + 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 at gmail.com>"); > +MODULE_DESCRIPTION("Simple firmware framebuffer DRM driver"); > +MODULE_ALIAS("platform:simple-framebuffer"); > diff --git a/drivers/gpu/drm/simpledrm/simpledrm_gem.c > b/drivers/gpu/drm/simpledrm/simpledrm_gem.c > new file mode 100644 > index 0000000..2d59632 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c > @@ -0,0 +1,276 @@ > +/* > + * SimpleDRM firmware framebuffer driver > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann at 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 <drm/drm_legacy.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_unlocked(&obj->base); > + return r; > + } > + > + /* handle owns a reference */ > + drm_gem_object_unreference_unlocked(&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(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; > + > + drm_vma_offset_lock_lookup(dev->vma_offset_manager); > + node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager, > + vma->vm_pgoff, > + vma_pages(vma)); > + drm_vma_offset_unlock_lookup(dev->vma_offset_manager); > + > + if (!node) > + return drm_legacy_mmap(filp, vma); > + else if (!drm_vma_node_is_allowed(node, filp)) > + return -EACCES; > + > + 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) > + return r; > + > + r = sdrm_gem_get_pages(obj); > + if (r < 0) > + return r; > + > + /* prevent dmabuf-imported mmap to user-space */ > + if (!obj->pages) > + return -EACCES; > + > + 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); > + return r; > + } > + } > + > + return 0; > +} > + > +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..6295a9f > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c > @@ -0,0 +1,276 @@ > +/* > + * SimpleDRM firmware framebuffer driver > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann at 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_atomic_helper.h> > +#include <drm/drm_crtc.h> > +#include <drm/drm_crtc_helper.h> > +#include "simpledrm.h" > + > +static const uint32_t sdrm_formats[] = { > + DRM_FORMAT_RGB565, > + DRM_FORMAT_ARGB8888, > + DRM_FORMAT_XRGB8888, > +}; > + > +static int sdrm_conn_get_modes(struct drm_connector *conn) > +{ > + struct sdrm_device *sdrm = conn->dev->dev_private; > + struct drm_display_mode *mode; > + > + mode = drm_cvt_mode(sdrm->ddev, sdrm->fb_width, sdrm->fb_height, > + 60, false, false, false); > + if (!mode) > + return 0; > + > + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; > + drm_mode_set_name(mode); > + drm_mode_probed_add(conn, mode); > + > + return 1; > +} > + > +static const struct drm_connector_helper_funcs sdrm_conn_hfuncs = { > + .get_modes = sdrm_conn_get_modes, > + .best_encoder = drm_atomic_helper_best_encoder, > +}; > + > +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 const struct drm_connector_funcs sdrm_conn_ops = { > + .dpms = drm_atomic_helper_connector_dpms, > + .reset = drm_atomic_helper_connector_reset, > + .detect = sdrm_conn_detect, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = drm_connector_cleanup, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > +}; > + > +static inline struct sdrm_device * > +pipe_to_sdrm(struct drm_simple_display_pipe *pipe) > +{ > + return container_of(pipe, struct sdrm_device, pipe); > +} > + > +static int sdrm_display_pipe_check(struct drm_simple_display_pipe *pipe, > + struct drm_plane_state *plane_state, > + struct drm_crtc_state *crtc_state) > +{ > + struct drm_display_mode *mode = &crtc_state->mode; > + struct sdrm_device *sdrm = pipe_to_sdrm(pipe); > + struct drm_framebuffer *fb = plane_state->fb; > + u32 x = plane_state->src_x >> 16; > + u32 y = plane_state->src_y >> 16; > + > + if (mode->hdisplay != sdrm->fb_width || > + mode->vdisplay != sdrm->fb_height) > + return -EINVAL; > + if (fb->width <= x || fb->height <= y || > + fb->width - x < sdrm->fb_width || fb->height - y < sdrm->fb_height) > + return -EINVAL; > + > + return 0; > +} > + > +void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe, > + struct drm_plane_state *plane_state) > +{ > + struct drm_framebuffer *fb = pipe->plane.state->fb; > + struct sdrm_device *sdrm = pipe_to_sdrm(pipe); > + > + if (fb) > + sdrm_dirty_all_locked(sdrm); > +} > + > +static void sdrm_crtc_send_vblank_event(struct drm_crtc *crtc) > +{ > + if (crtc->state && crtc->state->event) { > + spin_lock_irq(&crtc->dev->event_lock); > + drm_crtc_send_vblank_event(crtc, crtc->state->event); > + spin_unlock_irq(&crtc->dev->event_lock); > + crtc->state->event = NULL; > + } > +} > + > +static void sdrm_display_pipe_enable(struct drm_simple_display_pipe *pipe, > + struct drm_crtc_state *crtc_state) > +{ > + sdrm_crtc_send_vblank_event(&pipe->crtc); > +} > + > +static void sdrm_display_pipe_disable(struct drm_simple_display_pipe *pipe) > +{ > + sdrm_crtc_send_vblank_event(&pipe->crtc); > +} > + > +static const struct drm_simple_display_pipe_funcs sdrm_pipe_funcs = { > + .check = sdrm_display_pipe_check, > + .update = sdrm_display_pipe_update, > + .enable = sdrm_display_pipe_enable, > + .disable = sdrm_display_pipe_disable, > +}; > + > +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, > + const 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); > + > + gobj = drm_gem_object_lookup(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; > + } > + > + DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id, > + drm_get_format_name(fb->base.pixel_format)); > + > + 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, > + .atomic_check = drm_atomic_helper_check, > + .atomic_commit = drm_atomic_helper_commit, > +}; > + > +int sdrm_drm_modeset_init(struct sdrm_device *sdrm) > +{ > + struct drm_connector *conn = &sdrm->conn; > + struct drm_device *ddev = sdrm->ddev; > + int ret; > + > + 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.preferred_depth = sdrm->fb_bpp; > + ddev->mode_config.funcs = &sdrm_mode_config_ops; > + > + drm_connector_helper_add(conn, &sdrm_conn_hfuncs); > + ret = drm_connector_init(ddev, conn, &sdrm_conn_ops, > + DRM_MODE_CONNECTOR_VIRTUAL); > + if (ret) > + goto err_cleanup; > + > + ret = drm_mode_create_dirty_info_property(ddev); > + if (ret) > + goto err_cleanup; > + > + drm_object_attach_property(&conn->base, > + ddev->mode_config.dirty_info_property, > + DRM_MODE_DIRTY_ON); > + > + ret = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs, > + sdrm_formats, > + ARRAY_SIZE(sdrm_formats), conn); > + if (ret) > + goto err_cleanup; > + > + drm_mode_config_reset(ddev); > + > + return 0; > + > +err_cleanup: > + drm_mode_config_cleanup(ddev); > + > + return ret; > +} > -- > 2.8.2 >