[PATCH 1/2] drm: add SimpleDRM driver

Daniel Vetter daniel at ffwll.ch
Thu Aug 4 14:45:00 UTC 2016


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 at gmail.com
> Signed-off-by: Noralf Trønnes <noralf at 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 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
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


More information about the dri-devel mailing list