On Thu, Aug 04, 2016 at 04:03:19PM +0200, Noralf Trønnes wrote:
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@gmail.com Signed-off-by: Noralf Trønnes noralf@tronnes.org
Scrolled through it, only spotted the below nit (plus removing the legacy mmap stuff). But I'd like at least an ack from Dave.
+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;
drm_dev_register needs to be last, after setting the drvdata. -Daniel
- 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@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@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@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
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel