[RFC] drm: Add Grain Media GM12U320 kms driver

Daniel Vetter daniel at ffwll.ch
Tue Jun 20 08:57:55 UTC 2017


On Thu, Jun 01, 2017 at 01:59:34PM +0200, Hans de Goede wrote:
> Grain-media GM12U320 based devices are mini video projectors using USB for
> both power and video data transport.
> 
> This commit adds a kms driver for these devices, including prime support.
> 
> This driver is based on the existing udl kms driver, and the gm12u320
> fb driver by Viacheslav Nurmekhamitov <slavrn at yandex.ru>.
> 
> Signed-off-by: Hans de Goede <hdegoede at redhat.com>

Looks like something written yesterday (drm changes fast), bunch of
comments below where you need to modernize your driver. Should result in
piles of code removed.

Cheers, Daniel

> ---
>  MAINTAINERS                                   |   6 +
>  drivers/gpu/drm/Kconfig                       |   2 +
>  drivers/gpu/drm/Makefile                      |   1 +
>  drivers/gpu/drm/gm12u320/Kconfig              |  11 +
>  drivers/gpu/drm/gm12u320/Makefile             |   7 +
>  drivers/gpu/drm/gm12u320/gm12u320_connector.c | 148 ++++++++
>  drivers/gpu/drm/gm12u320/gm12u320_dmabuf.c    | 283 ++++++++++++++
>  drivers/gpu/drm/gm12u320/gm12u320_drv.c       | 157 ++++++++
>  drivers/gpu/drm/gm12u320/gm12u320_drv.h       | 129 +++++++
>  drivers/gpu/drm/gm12u320/gm12u320_encoder.c   |  75 ++++
>  drivers/gpu/drm/gm12u320/gm12u320_fb.c        | 343 +++++++++++++++++
>  drivers/gpu/drm/gm12u320/gm12u320_gem.c       | 237 ++++++++++++
>  drivers/gpu/drm/gm12u320/gm12u320_main.c      | 506 ++++++++++++++++++++++++++
>  drivers/gpu/drm/gm12u320/gm12u320_modeset.c   | 143 ++++++++
>  14 files changed, 2048 insertions(+)
>  create mode 100644 drivers/gpu/drm/gm12u320/Kconfig
>  create mode 100644 drivers/gpu/drm/gm12u320/Makefile
>  create mode 100644 drivers/gpu/drm/gm12u320/gm12u320_connector.c
>  create mode 100644 drivers/gpu/drm/gm12u320/gm12u320_dmabuf.c
>  create mode 100644 drivers/gpu/drm/gm12u320/gm12u320_drv.c
>  create mode 100644 drivers/gpu/drm/gm12u320/gm12u320_drv.h
>  create mode 100644 drivers/gpu/drm/gm12u320/gm12u320_encoder.c
>  create mode 100644 drivers/gpu/drm/gm12u320/gm12u320_fb.c
>  create mode 100644 drivers/gpu/drm/gm12u320/gm12u320_gem.c
>  create mode 100644 drivers/gpu/drm/gm12u320/gm12u320_main.c
>  create mode 100644 drivers/gpu/drm/gm12u320/gm12u320_modeset.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 053c3bdd1fe5..e1973afc428a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4423,6 +4423,12 @@ S:	Supported
>  F:	drivers/gpu/drm/nouveau/
>  F:	include/uapi/drm/nouveau_drm.h
>  
> +DRM DRIVERS FOR GRAIN-MEDIA GM12U320
> +M:	Hans de Goede <hdegoede at redhat.com>
> +L:	dri-devel at lists.freedesktop.org
> +S:	Supported
> +F:	drivers/gpu/drm/gm12u320/
> +
>  DRM DRIVERS FOR NVIDIA TEGRA
>  M:	Thierry Reding <thierry.reding at gmail.com>
>  L:	dri-devel at lists.freedesktop.org
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 78d7fc0ebb57..46898c4dd0d6 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -274,6 +274,8 @@ source "drivers/gpu/drm/meson/Kconfig"
>  
>  source "drivers/gpu/drm/tinydrm/Kconfig"
>  
> +source "drivers/gpu/drm/gm12u320/Kconfig"
> +
>  # Keep legacy drivers last
>  
>  menuconfig DRM_LEGACY
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 59f0f9b696eb..cdec41c52e91 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -96,3 +96,4 @@ obj-y			+= hisilicon/
>  obj-$(CONFIG_DRM_ZTE)	+= zte/
>  obj-$(CONFIG_DRM_MXSFB)	+= mxsfb/
>  obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
> +obj-$(CONFIG_DRM_GM12U320) += gm12u320/
> diff --git a/drivers/gpu/drm/gm12u320/Kconfig b/drivers/gpu/drm/gm12u320/Kconfig
> new file mode 100644
> index 000000000000..67274cb291ed
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/Kconfig
> @@ -0,0 +1,11 @@
> +config DRM_GM12U320
> +	tristate "Grain Media GM12U320 based mini projectors"
> +	depends on DRM
> +	depends on USB_SUPPORT
> +	depends on USB_ARCH_HAS_HCD
> +	select USB
> +	select DRM_KMS_HELPER
> +	help
> +	  This is a KMS driver for Grain Media GM12U320 based usb mini video
> +	  projectors, such as the Acer C120.
> +	  Say M/Y to add support for these devices via drm/kms interfaces.
> diff --git a/drivers/gpu/drm/gm12u320/Makefile b/drivers/gpu/drm/gm12u320/Makefile
> new file mode 100644
> index 000000000000..6234473967b2
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/Makefile
> @@ -0,0 +1,7 @@
> +ccflags-y := -Iinclude/drm
> +
> +gm12u320-y :=	gm12u320_drv.o gm12u320_modeset.o gm12u320_connector.o \
> +		gm12u320_encoder.o gm12u320_main.o gm12u320_fb.o \
> +		gm12u320_gem.o gm12u320_dmabuf.o
> +
> +obj-$(CONFIG_DRM_GM12U320) += gm12u320.o
> diff --git a/drivers/gpu/drm/gm12u320/gm12u320_connector.c b/drivers/gpu/drm/gm12u320/gm12u320_connector.c
> new file mode 100644
> index 000000000000..042da07c16c2
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/gm12u320_connector.c
> @@ -0,0 +1,148 @@
> +/*
> + * Copyright (C) 2012-2016 Red Hat Inc.
> + *
> + * Based in parts on the udl code. Based in parts on the gm12u320 fb driver:
> + * Copyright (C) 2013 Viacheslav Nurmekhamitov <slavrn at yandex.ru>
> + * Copyright (C) 2009 Roberto De Ioris <roberto at unbit.it>
> + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml at gmail.com>
> + * Copyright (C) 2009 Bernie Thompson <bernie at plugable.com>
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License v2. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_crtc_helper.h>
> +#include "gm12u320_drv.h"
> +
> +/*
> + * Note this assumes this driver is only ever used with the Acer C120, if we
> + * add support for other devices the vendor and model should be parameterized.
> + */
> +static struct edid gm12u320_edid = {
> +	.header		= { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 },
> +	.mfg_id		= { 0x04, 0x72 },	/* "ACR" */
> +	.prod_code	= { 0x20, 0xc1 },	/* C120h */
> +	.mfg_week	= 1,
> +	.mfg_year	= 1,
> +	.version	= 1,			/* EDID 1.3 */
> +	.revision	= 3,			/* EDID 1.3 */
> +	.input		= 0x80,			/* Digital input */
> +	.features	= 0x02,			/* Pref timing in DTD 1 */
> +	.standard_timings = { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 },
> +			      { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 } },
> +	.detailed_timings = { {
> +		.pixel_clock = 3383,
> +		/* hactive = 852, hblank = 256 */
> +		.data.pixel_data.hactive_lo = 0x54,
> +		.data.pixel_data.hblank_lo = 0x00,
> +		.data.pixel_data.hactive_hblank_hi = 0x31,
> +		/* vactive = 480, vblank = 28 */
> +		.data.pixel_data.vactive_lo = 0xe0,
> +		.data.pixel_data.vblank_lo = 0x1c,
> +		.data.pixel_data.vactive_vblank_hi = 0x10,
> +		/* hsync offset 40 pw 128, vsync offset 1 pw 4 */
> +		.data.pixel_data.hsync_offset_lo = 0x28,
> +		.data.pixel_data.hsync_pulse_width_lo = 0x80,
> +		.data.pixel_data.vsync_offset_pulse_width_lo = 0x14,
> +		.data.pixel_data.hsync_vsync_offset_pulse_width_hi = 0x00,
> +		/* Digital separate syncs, hsync+, vsync+ */
> +		.data.pixel_data.misc = 0x1e,
> +	}, {
> +		.pixel_clock = 0,
> +		.data.other_data.type = 0xfd, /* Monitor ranges */
> +		.data.other_data.data.range.min_vfreq = 59,
> +		.data.other_data.data.range.max_vfreq = 61,
> +		.data.other_data.data.range.min_hfreq_khz = 29,
> +		.data.other_data.data.range.max_hfreq_khz = 32,
> +		.data.other_data.data.range.pixel_clock_mhz = 4, /* 40 MHz */
> +		.data.other_data.data.range.flags = 0,
> +		.data.other_data.data.range.formula.cvt = {
> +			0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 },
> +	}, {
> +		.pixel_clock = 0,
> +		.data.other_data.type = 0xfc, /* Model string */
> +		.data.other_data.data.str.str = {
> +			'C', '1', '2', '0', 'P', 'r', 'o', 'j', 'e', 'c',
> +			't', 'o', 'r' },
> +	}, {
> +		.pixel_clock = 0,
> +		.data.other_data.type = 0xfe, /* Unspecified text / padding */
> +		.data.other_data.data.str.str = {
> +			'\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
> +			' ', ' ',  ' ' },
> +	} },
> +	.checksum = 0x40,
> +};
> +
> +static int gm12u320_get_modes(struct drm_connector *connector)
> +{
> +	drm_mode_connector_update_edid_property(connector, &gm12u320_edid);
> +	return drm_add_edid_modes(connector, &gm12u320_edid);
> +}
> +
> +static enum drm_connector_status
> +gm12u320_detect(struct drm_connector *connector, bool force)
> +{
> +	if (drm_device_is_unplugged(connector->dev))
> +		return connector_status_disconnected;
> +
> +	return connector_status_connected;
> +}
> +
> +static struct drm_encoder*
> +gm12u320_best_single_encoder(struct drm_connector *connector)
> +{
> +	int enc_id = connector->encoder_ids[0];
> +
> +	return drm_encoder_find(connector->dev, enc_id);
> +}
> +
> +static int gm12u320_connector_set_property(struct drm_connector *connector,
> +					   struct drm_property *property,
> +					   uint64_t val)
> +{
> +	return 0;
> +}
> +
> +static void gm12u320_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +	kfree(connector);
> +}
> +
> +static const struct drm_connector_helper_funcs gm12u320_helper_funcs = {
> +	.get_modes = gm12u320_get_modes,
> +	.best_encoder = gm12u320_best_single_encoder,
> +};
> +
> +static const struct drm_connector_funcs gm12u320_connector_funcs = {
> +	.dpms = drm_helper_connector_dpms,
> +	.detect = gm12u320_detect,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = gm12u320_connector_destroy,
> +	.set_property = gm12u320_connector_set_property,
> +};
> +
> +int gm12u320_connector_init(struct drm_device *dev,
> +			    struct drm_encoder *encoder)
> +{
> +	struct drm_connector *connector;
> +
> +	connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL);
> +	if (!connector)
> +		return -ENOMEM;
> +
> +	drm_connector_init(dev, connector, &gm12u320_connector_funcs,
> +			   DRM_MODE_CONNECTOR_Unknown);
> +	drm_connector_helper_add(connector, &gm12u320_helper_funcs);
> +
> +	drm_connector_register(connector);
> +	drm_mode_connector_attach_encoder(connector, encoder);
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/gm12u320/gm12u320_dmabuf.c b/drivers/gpu/drm/gm12u320/gm12u320_dmabuf.c
> new file mode 100644
> index 000000000000..b55861e8323c
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/gm12u320_dmabuf.c
> @@ -0,0 +1,283 @@
> +/*
> + * Based on the udl dmabuf code:
> + *
> + * Copyright (c) 2014 The Chromium OS Authors
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <drm/drmP.h>
> +#include "gm12u320_drv.h"
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +
> +struct gm12u320_drm_dmabuf_attachment {
> +	struct sg_table sgt;
> +	enum dma_data_direction dir;
> +	bool is_mapped;
> +};
> +
> +static int gm12u320_attach_dma_buf(struct dma_buf *dmabuf,
> +				   struct device *dev,
> +				   struct dma_buf_attachment *attach)
> +{
> +	struct gm12u320_drm_dmabuf_attachment *gm12u320_attach;
> +
> +	DRM_DEBUG_PRIME("[DEV:%s] size:%zd\n", dev_name(attach->dev),
> +			attach->dmabuf->size);
> +
> +	gm12u320_attach = kzalloc(sizeof(*gm12u320_attach), GFP_KERNEL);
> +	if (!gm12u320_attach)
> +		return -ENOMEM;
> +
> +	gm12u320_attach->dir = DMA_NONE;
> +	attach->priv = gm12u320_attach;
> +
> +	return 0;
> +}
> +
> +static void gm12u320_detach_dma_buf(struct dma_buf *dmabuf,
> +				    struct dma_buf_attachment *attach)
> +{
> +	struct gm12u320_drm_dmabuf_attachment *gm12u320_attach = attach->priv;
> +	struct sg_table *sgt;
> +
> +	if (!gm12u320_attach)
> +		return;
> +
> +	DRM_DEBUG_PRIME("[DEV:%s] size:%zd\n", dev_name(attach->dev),
> +			attach->dmabuf->size);
> +
> +	sgt = &gm12u320_attach->sgt;
> +
> +	if (gm12u320_attach->dir != DMA_NONE)
> +		dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
> +				gm12u320_attach->dir);
> +
> +	sg_free_table(sgt);
> +	kfree(gm12u320_attach);
> +	attach->priv = NULL;
> +}
> +
> +static struct sg_table *gm12u320_map_dma_buf(struct dma_buf_attachment *attach,
> +					     enum dma_data_direction dir)
> +{
> +	struct gm12u320_drm_dmabuf_attachment *gm12u320_attach = attach->priv;
> +	struct gm12u320_gem_object *obj = to_gm12u320_bo(attach->dmabuf->priv);
> +	struct drm_device *dev = obj->base.dev;
> +	struct scatterlist *rd, *wr;
> +	struct sg_table *sgt = NULL;
> +	unsigned int i;
> +	int page_count;
> +	int nents, ret;
> +
> +	DRM_DEBUG_PRIME("[DEV:%s] size:%zd dir=%d\n", dev_name(attach->dev),
> +			attach->dmabuf->size, dir);
> +
> +	/* just return current sgt if already requested. */
> +	if (gm12u320_attach->dir == dir && gm12u320_attach->is_mapped)
> +		return &gm12u320_attach->sgt;
> +
> +	if (!obj->pages) {
> +		ret = gm12u320_gem_get_pages(obj);
> +		if (ret) {
> +			DRM_ERROR("failed to map pages.\n");
> +			return ERR_PTR(ret);
> +		}
> +	}
> +
> +	page_count = obj->base.size / PAGE_SIZE;
> +	obj->sg = drm_prime_pages_to_sg(obj->pages, page_count);
> +	if (IS_ERR(obj->sg)) {
> +		DRM_ERROR("failed to allocate sgt.\n");
> +		return ERR_CAST(obj->sg);
> +	}
> +
> +	sgt = &gm12u320_attach->sgt;
> +
> +	ret = sg_alloc_table(sgt, obj->sg->orig_nents, GFP_KERNEL);
> +	if (ret) {
> +		DRM_ERROR("failed to alloc sgt.\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	mutex_lock(&dev->struct_mutex);
> +
> +	rd = obj->sg->sgl;
> +	wr = sgt->sgl;
> +	for (i = 0; i < sgt->orig_nents; ++i) {
> +		sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
> +		rd = sg_next(rd);
> +		wr = sg_next(wr);
> +	}
> +
> +	if (dir != DMA_NONE) {
> +		nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir);
> +		if (!nents) {
> +			DRM_ERROR("failed to map sgl with iommu.\n");
> +			sg_free_table(sgt);
> +			sgt = ERR_PTR(-EIO);
> +			goto err_unlock;
> +		}
> +	}
> +
> +	gm12u320_attach->is_mapped = true;
> +	gm12u320_attach->dir = dir;
> +	attach->priv = gm12u320_attach;
> +
> +err_unlock:
> +	mutex_unlock(&dev->struct_mutex);
> +	return sgt;
> +}
> +
> +static void gm12u320_unmap_dma_buf(struct dma_buf_attachment *attach,
> +				   struct sg_table *sgt,
> +				   enum dma_data_direction dir)
> +{
> +	/* Nothing to do. */
> +	DRM_DEBUG_PRIME("[DEV:%s] size:%zd dir:%d\n", dev_name(attach->dev),
> +			attach->dmabuf->size, dir);
> +}
> +
> +static void *gm12u320_dmabuf_kmap(struct dma_buf *dma_buf,
> +				  unsigned long page_num)
> +{
> +	/* TODO */
> +
> +	return NULL;
> +}
> +
> +static void *gm12u320_dmabuf_kmap_atomic(struct dma_buf *dma_buf,
> +					 unsigned long page_num)
> +{
> +	/* TODO */
> +
> +	return NULL;
> +}
> +
> +static void gm12u320_dmabuf_kunmap(struct dma_buf *dma_buf,
> +				   unsigned long page_num, void *addr)
> +{
> +	/* TODO */
> +}
> +
> +static void gm12u320_dmabuf_kunmap_atomic(struct dma_buf *dma_buf,
> +					  unsigned long page_num,
> +					  void *addr)
> +{
> +	/* TODO */
> +}
> +
> +static int gm12u320_dmabuf_mmap(struct dma_buf *dma_buf,
> +				struct vm_area_struct *vma)
> +{
> +	/* TODO */
> +
> +	return -EINVAL;
> +}

I'm not a friend of dummy functions and todos. The core should reject this
all correctly for you, please remove these hooks. Or fix up the core to do
that for you.

> +static struct dma_buf_ops gm12u320_dmabuf_ops = {
> +	.attach			= gm12u320_attach_dma_buf,
> +	.detach			= gm12u320_detach_dma_buf,
> +	.map_dma_buf		= gm12u320_map_dma_buf,
> +	.unmap_dma_buf		= gm12u320_unmap_dma_buf,
> +	.map			= gm12u320_dmabuf_kmap,
> +	.map_atomic		= gm12u320_dmabuf_kmap_atomic,
> +	.unmap			= gm12u320_dmabuf_kunmap,
> +	.unmap_atomic		= gm12u320_dmabuf_kunmap_atomic,
> +	.mmap			= gm12u320_dmabuf_mmap,
> +	.release		= drm_gem_dmabuf_release,
> +};
> +
> +struct dma_buf *gm12u320_gem_prime_export(struct drm_device *dev,
> +				     struct drm_gem_object *obj, int flags)
> +{
> +	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
> +
> +	exp_info.ops = &gm12u320_dmabuf_ops;
> +	exp_info.size = obj->size;
> +	exp_info.flags = flags;
> +	exp_info.priv = obj;
> +
> +	return drm_gem_dmabuf_export(dev, &exp_info);
> +}
> +
> +static int gm12u320_prime_create(struct drm_device *dev,
> +				 size_t size, struct sg_table *sg,
> +				 struct gm12u320_gem_object **obj_p)
> +{
> +	struct gm12u320_gem_object *obj;
> +	int npages;
> +
> +	npages = size / PAGE_SIZE;
> +
> +	*obj_p = NULL;
> +	obj = gm12u320_gem_alloc_object(dev, npages * PAGE_SIZE);
> +	if (!obj)
> +		return -ENOMEM;
> +
> +	obj->sg = sg;
> +	obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
> +	if (obj->pages == NULL) {
> +		DRM_ERROR("obj pages is NULL %d\n", npages);
> +		return -ENOMEM;
> +	}
> +
> +	drm_prime_sg_to_page_addr_arrays(sg, obj->pages, NULL, npages);
> +
> +	*obj_p = obj;
> +	return 0;
> +}
> +
> +struct drm_gem_object *gm12u320_gem_prime_import(struct drm_device *dev,
> +						 struct dma_buf *dma_buf)
> +{
> +	struct dma_buf_attachment *attach;
> +	struct sg_table *sg;
> +	struct gm12u320_gem_object *uobj;
> +	int ret;
> +
> +	/* need to attach */
> +	get_device(dev->dev);
> +	attach = dma_buf_attach(dma_buf, dev->dev);
> +	if (IS_ERR(attach)) {
> +		put_device(dev->dev);
> +		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;
> +	}
> +
> +	ret = gm12u320_prime_create(dev, dma_buf->size, sg, &uobj);
> +	if (ret)
> +		goto fail_unmap;
> +
> +	uobj->base.import_attach = attach;
> +	uobj->flags = GM12U320_BO_WC;
> +
> +	return &uobj->base;
> +
> +fail_unmap:
> +	dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
> +fail_detach:
> +	dma_buf_detach(dma_buf, attach);
> +	dma_buf_put(dma_buf);
> +	put_device(dev->dev);
> +	return ERR_PTR(ret);
> +}

Not sure, but is there some room for more shared helpers for non-cma gem
objects when using prime? For cma this is all dead easy, but we now also
have a pile of simple shmem-gem drivers that would justify a bit more
helpers here I think ...

Can be a follow-up thing ofc.

> diff --git a/drivers/gpu/drm/gm12u320/gm12u320_drv.c b/drivers/gpu/drm/gm12u320/gm12u320_drv.c
> new file mode 100644
> index 000000000000..eb1e52a9b485
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/gm12u320_drv.c
> @@ -0,0 +1,157 @@
> +/*
> + * Copyright (C) 2012-2016 Red Hat Inc.
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License v2. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <linux/module.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include "gm12u320_drv.h"
> +
> +static int gm12u320_driver_set_busid(struct drm_device *d, struct drm_master *m)
> +{
> +	return 0;
> +}

This just died.

> +
> +static const struct vm_operations_struct gm12u320_gem_vm_ops = {
> +	.fault = gm12u320_gem_fault,
> +	.open = drm_gem_vm_open,
> +	.close = drm_gem_vm_close,
> +};
> +
> +static const struct file_operations gm12u320_driver_fops = {
> +	.owner = THIS_MODULE,
> +	.open = drm_open,
> +	.mmap = gm12u320_drm_gem_mmap,
> +	.poll = drm_poll,
> +	.read = drm_read,
> +	.unlocked_ioctl	= drm_ioctl,
> +	.release = drm_release,
> +	.compat_ioctl = drm_compat_ioctl,
> +	.llseek = noop_llseek,
> +};
> +
> +static struct drm_driver driver = {
> +	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
> +	.load = gm12u320_driver_load,
> +	.unload = gm12u320_driver_unload,

load/unload are deprecated, pls read the kerneldoc for how this should be
done using drm_dev_init and friends.

> +	.set_busid = gm12u320_driver_set_busid,
> +
> +	/* gem hooks */
> +	.gem_free_object = gm12u320_gem_free_object,
> +	.gem_vm_ops = &gm12u320_gem_vm_ops,
> +
> +	.dumb_create = gm12u320_dumb_create,
> +	.dumb_map_offset = gm12u320_gem_mmap,
> +	.dumb_destroy = drm_gem_dumb_destroy,
> +	.fops = &gm12u320_driver_fops,
> +
> +	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +	.gem_prime_export = gm12u320_gem_prime_export,
> +	.gem_prime_import = gm12u320_gem_prime_import,
> +
> +	.name = DRIVER_NAME,
> +	.desc = DRIVER_DESC,
> +	.date = DRIVER_DATE,
> +	.major = DRIVER_MAJOR,
> +	.minor = DRIVER_MINOR,
> +	.patchlevel = DRIVER_PATCHLEVEL,
> +};
> +
> +static int gm12u320_usb_probe(struct usb_interface *interface,
> +			      const struct usb_device_id *id)
> +{
> +	struct usb_device *udev = interface_to_usbdev(interface);
> +	struct drm_device *dev;
> +	int r;
> +
> +	/*
> +	 * The gm12u320 presents itself to the system as 2 usb mass-storage
> +	 * interfaces, for the second one we proceed successully with binding,
> +	 * but otherwise ignore it.
> +	 */
> +	if (interface->cur_altsetting->desc.bInterfaceNumber != 0)
> +		return 0;
> +
> +	dev = drm_dev_alloc(&driver, &interface->dev);
> +	if (IS_ERR(dev))
> +		return PTR_ERR(dev);
> +
> +	r = drm_dev_register(dev, (unsigned long)udev);
> +	if (r)
> +		goto err_free;
> +
> +	usb_set_intfdata(interface, dev);
> +	DRM_INFO("Initialized gm12u320 on minor %d\n", dev->primary->index);
> +
> +	return 0;
> +
> +err_free:
> +	drm_dev_unref(dev);
> +	return r;
> +}
> +
> +static void gm12u320_usb_disconnect(struct usb_interface *interface)
> +{
> +	struct drm_device *dev = usb_get_intfdata(interface);
> +
> +	if (!dev)
> +		return;
> +
> +	drm_kms_helper_poll_disable(dev);
> +	gm12u320_fbdev_unplug(dev);
> +	gm12u320_stop_fb_update(dev);
> +	drm_unplug_dev(dev);
> +}
> +
> +#ifdef CONFIG_PM
> +
> +int gm12u320_suspend(struct usb_interface *interface, pm_message_t message)
> +{
> +	struct drm_device *dev = usb_get_intfdata(interface);
> +
> +	if (!dev)
> +		return 0;
> +
> +	gm12u320_stop_fb_update(dev);
> +	return 0;
> +}
> +
> +int gm12u320_resume(struct usb_interface *interface)
> +{
> +	struct drm_device *dev = usb_get_intfdata(interface);
> +
> +	if (!dev)
> +		return 0;
> +
> +	gm12u320_set_ecomode(dev);
> +	gm12u320_start_fb_update(dev);
> +	return 0;
> +}
> +#endif
> +
> +static struct usb_device_id id_table[] = {
> +	{ USB_DEVICE(0x1de1, 0xc102) },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(usb, id_table);
> +
> +static struct usb_driver gm12u320_driver = {
> +	.name = "gm12u320",
> +	.probe = gm12u320_usb_probe,
> +	.disconnect = gm12u320_usb_disconnect,
> +	.id_table = id_table,
> +#ifdef CONFIG_PM
> +	.suspend = gm12u320_suspend,
> +	.resume = gm12u320_resume,
> +	.reset_resume = gm12u320_resume,
> +#endif
> +};
> +
> +module_usb_driver(gm12u320_driver);
> +MODULE_AUTHOR("Hans de Goede <hdegoede at redhat.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/gm12u320/gm12u320_drv.h b/drivers/gpu/drm/gm12u320/gm12u320_drv.h
> new file mode 100644
> index 000000000000..8955a2ad753a
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/gm12u320_drv.h
> @@ -0,0 +1,129 @@
> +/*
> + * Copyright (C) 2012-2016 Red Hat Inc.
> + *
> + * Based in parts on the udl code. Based in parts on the gm12u320 fb driver:
> + * Copyright (C) 2013 Viacheslav Nurmekhamitov <slavrn at yandex.ru>
> + * Copyright (C) 2009 Roberto De Ioris <roberto at unbit.it>
> + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml at gmail.com>
> + * Copyright (C) 2009 Bernie Thompson <bernie at plugable.com>
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License v2. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#ifndef GM12U320_DRV_H
> +#define GM12U320_DRV_H
> +
> +#include <linux/usb.h>
> +#include <linux/spinlock.h>
> +#include <drm/drm_gem.h>
> +
> +#define DRIVER_NAME		"gm12u320"
> +#define DRIVER_DESC		"Grain Media GM12U320 USB projector display"
> +#define DRIVER_DATE		"20150107"
> +
> +#define DRIVER_MAJOR		0
> +#define DRIVER_MINOR		0
> +#define DRIVER_PATCHLEVEL	1
> +
> +#define GM12U320_BO_CACHEABLE	(1 << 0)
> +#define GM12U320_BO_WC		(1 << 1)
> +
> +/*
> + * The DLP has an actual width of 854 pixels, but that is not a multiple
> + * of 4, breaking things left and right, so we export a width of 852.
> + */
> +#define GM12U320_USER_WIDTH	852
> +#define GM12U320_REAL_WIDTH	854
> +#define GM12U320_HEIGHT		480
> +
> +#define GM12U320_BLOCK_COUNT	20
> +
> +struct gm12u320_device;
> +
> +struct gm12u320_fbdev;
> +
> +struct gm12u320_device {
> +	struct device *dev;
> +	struct usb_device *udev;
> +	struct drm_device *ddev;
> +	struct gm12u320_fbdev *fbdev;
> +	unsigned char *cmd_buf;
> +	unsigned char *data_buf[GM12U320_BLOCK_COUNT];
> +	struct {
> +		bool run;
> +		struct workqueue_struct *workq;
> +		struct work_struct work;
> +		wait_queue_head_t waitq;
> +		struct mutex lock;
> +		struct gm12u320_framebuffer *fb;
> +		int x1;
> +		int x2;
> +		int y1;
> +		int y2;
> +	} fb_update;
> +};
> +
> +struct gm12u320_gem_object {
> +	struct drm_gem_object base;
> +	struct page **pages;
> +	void *vmapping;
> +	struct sg_table *sg;
> +	unsigned int flags;
> +};
> +
> +#define to_gm12u320_bo(x) container_of(x, struct gm12u320_gem_object, base)
> +
> +struct gm12u320_framebuffer {
> +	struct drm_framebuffer base;
> +	struct gm12u320_gem_object *obj;
> +};
> +
> +#define to_gm12u320_fb(x) container_of(x, struct gm12u320_framebuffer, base)
> +
> +/* modeset */
> +int gm12u320_modeset_init(struct drm_device *dev);
> +void gm12u320_modeset_cleanup(struct drm_device *dev);
> +int gm12u320_connector_init(struct drm_device *dev,
> +			    struct drm_encoder *encoder);
> +
> +struct drm_encoder *gm12u320_encoder_init(struct drm_device *dev);
> +
> +int gm12u320_driver_load(struct drm_device *dev, unsigned long flags);
> +void gm12u320_driver_unload(struct drm_device *dev);
> +
> +int gm12u320_fbdev_init(struct drm_device *dev);
> +void gm12u320_fbdev_cleanup(struct drm_device *dev);
> +void gm12u320_fbdev_unplug(struct drm_device *dev);
> +struct drm_framebuffer *
> +gm12u320_fb_user_fb_create(struct drm_device *dev, struct drm_file *file,
> +			   const struct drm_mode_fb_cmd2 *mode_cmd);
> +
> +int gm12u320_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
> +			 struct drm_mode_create_dumb *args);
> +int gm12u320_gem_mmap(struct drm_file *file_priv, struct drm_device *dev,
> +		      uint32_t handle, uint64_t *offset);
> +
> +struct gm12u320_gem_object *
> +gm12u320_gem_alloc_object(struct drm_device *dev, size_t size);
> +void gm12u320_gem_free_object(struct drm_gem_object *gem_obj);
> +struct dma_buf *gm12u320_gem_prime_export(struct drm_device *dev,
> +				      struct drm_gem_object *obj, int flags);
> +struct drm_gem_object *gm12u320_gem_prime_import(struct drm_device *dev,
> +						 struct dma_buf *dma_buf);
> +
> +int gm12u320_gem_get_pages(struct gm12u320_gem_object *obj);
> +void gm12u320_gem_put_pages(struct gm12u320_gem_object *obj);
> +int gm12u320_gem_vmap(struct gm12u320_gem_object *obj);
> +void gm12u320_gem_vunmap(struct gm12u320_gem_object *obj);
> +int gm12u320_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
> +int gm12u320_gem_fault(struct vm_fault *vmf);
> +
> +void gm12u320_fb_mark_dirty(struct gm12u320_framebuffer *fb,
> +			    int x1, int x2, int y1, int y2);
> +void gm12u320_start_fb_update(struct drm_device *dev);
> +void gm12u320_stop_fb_update(struct drm_device *dev);
> +int gm12u320_set_ecomode(struct drm_device *dev);
> +
> +#endif
> diff --git a/drivers/gpu/drm/gm12u320/gm12u320_encoder.c b/drivers/gpu/drm/gm12u320/gm12u320_encoder.c
> new file mode 100644
> index 000000000000..085f05b06674
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/gm12u320_encoder.c
> @@ -0,0 +1,75 @@
> +/*
> + * Copyright (C) 2012-2016 Red Hat Inc.
> + *
> + * Based in parts on the udl code. Based in parts on the gm12u320 fb driver:
> + * Copyright (C) 2013 Viacheslav Nurmekhamitov <slavrn at yandex.ru>
> + * Copyright (C) 2009 Roberto De Ioris <roberto at unbit.it>
> + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml at gmail.com>
> + * Copyright (C) 2009 Bernie Thompson <bernie at plugable.com>
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License v2. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include "gm12u320_drv.h"
> +
> +/* dummy encoder */
> +static void gm12u320_enc_destroy(struct drm_encoder *encoder)
> +{
> +	drm_encoder_cleanup(encoder);
> +	kfree(encoder);
> +}
> +
> +static void gm12u320_encoder_disable(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void gm12u320_encoder_prepare(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void gm12u320_encoder_commit(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void gm12u320_encoder_mode_set(struct drm_encoder *encoder,
> +				 struct drm_display_mode *mode,
> +				 struct drm_display_mode *adjusted_mode)
> +{
> +}
> +
> +static void
> +gm12u320_encoder_dpms(struct drm_encoder *encoder, int mode)
> +{
> +}
> +
> +static const struct drm_encoder_helper_funcs gm12u320_helper_funcs = {
> +	.dpms = gm12u320_encoder_dpms,
> +	.prepare = gm12u320_encoder_prepare,
> +	.mode_set = gm12u320_encoder_mode_set,
> +	.commit = gm12u320_encoder_commit,
> +	.disable = gm12u320_encoder_disable,
> +};
> +
> +static const struct drm_encoder_funcs gm12u320_enc_funcs = {
> +	.destroy = gm12u320_enc_destroy,
> +};
> +
> +struct drm_encoder *gm12u320_encoder_init(struct drm_device *dev)
> +{
> +	struct drm_encoder *encoder;
> +
> +	encoder = kzalloc(sizeof(struct drm_encoder), GFP_KERNEL);
> +	if (!encoder)
> +		return NULL;
> +
> +	drm_encoder_init(dev, encoder, &gm12u320_enc_funcs,
> +			 DRM_MODE_ENCODER_TMDS, NULL);
> +	drm_encoder_helper_add(encoder, &gm12u320_helper_funcs);
> +	encoder->possible_crtcs = 1;
> +	return encoder;
> +}
> diff --git a/drivers/gpu/drm/gm12u320/gm12u320_fb.c b/drivers/gpu/drm/gm12u320/gm12u320_fb.c
> new file mode 100644
> index 000000000000..8b7605f76af9
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/gm12u320_fb.c
> @@ -0,0 +1,343 @@
> +/*
> + * Copyright (C) 2012-2016 Red Hat Inc.
> + *
> + * Based in parts on the udl code. Based in parts on the gm12u320 fb driver:
> + * Copyright (C) 2013 Viacheslav Nurmekhamitov <slavrn at yandex.ru>
> + * Copyright (C) 2009 Roberto De Ioris <roberto at unbit.it>
> + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml at gmail.com>
> + * Copyright (C) 2009 Bernie Thompson <bernie at plugable.com>
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License v2. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/fb.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include "gm12u320_drv.h"
> +
> +#include <drm/drm_fb_helper.h>
> +
> +struct gm12u320_fbdev {
> +	struct drm_fb_helper helper;
> +	struct gm12u320_framebuffer fb;
> +};
> +
> +void gm12u320_fb_mark_dirty(struct gm12u320_framebuffer *fb,
> +			    int x1, int x2, int y1, int y2)
> +{
> +	struct drm_device *dev = fb->base.dev;
> +	struct gm12u320_device *gm12u320 = dev->dev_private;
> +	struct gm12u320_framebuffer *old_fb = NULL;
> +	bool wakeup = false;
> +
> +	mutex_lock(&gm12u320->fb_update.lock);
> +
> +	if (gm12u320->fb_update.fb != fb) {
> +		gm12u320->fb_update.x1 = x1;
> +		gm12u320->fb_update.x2 = x2;
> +		gm12u320->fb_update.y1 = y1;
> +		gm12u320->fb_update.y2 = y2;
> +		old_fb = gm12u320->fb_update.fb;
> +		gm12u320->fb_update.fb = fb;
> +		drm_framebuffer_reference(&gm12u320->fb_update.fb->base);
> +		wakeup = true;
> +	} else {
> +		gm12u320->fb_update.x1 = min(gm12u320->fb_update.x1, x1);
> +		gm12u320->fb_update.x2 = max(gm12u320->fb_update.x2, x2);
> +		gm12u320->fb_update.y1 = min(gm12u320->fb_update.y1, y1);
> +		gm12u320->fb_update.y2 = max(gm12u320->fb_update.y2, y2);
> +	}
> +
> +	mutex_unlock(&gm12u320->fb_update.lock);
> +
> +	if (wakeup)
> +		wake_up(&gm12u320->fb_update.waitq);
> +
> +	if (old_fb)
> +		drm_framebuffer_unreference(&old_fb->base);
> +}
> +
> +static int gm12u320_fb_open(struct fb_info *info, int user)
> +{
> +	struct gm12u320_fbdev *fbdev = info->par;
> +	struct drm_device *ddev = fbdev->fb.base.dev;
> +
> +	/* If the USB device is gone, we don't accept new opens */
> +	if (drm_device_is_unplugged(ddev))
> +		return -ENODEV;
> +
> +	return 0;
> +}
> +
> +static struct fb_ops gm12u320_fb_ops = {
> +	.owner = THIS_MODULE,
> +	DRM_FB_HELPER_DEFAULT_OPS,
> +	.fb_fillrect = drm_fb_helper_sys_fillrect,
> +	.fb_copyarea = drm_fb_helper_sys_copyarea,
> +	.fb_imageblit = drm_fb_helper_sys_imageblit,
> +	.fb_open = gm12u320_fb_open,
> +};
> +
> +#ifdef CONFIG_DRM_FBDEV_EMULATION
> +static struct fb_deferred_io gm12u320_fb_defio = {
> +	.delay = HZ / 30,
> +	.deferred_io = drm_fb_helper_deferred_io,
> +};
> +#endif
> +
> +static int gm12u320_user_framebuffer_dirty(struct drm_framebuffer *drm_fb,
> +					   struct drm_file *file,
> +					   unsigned flags, unsigned color,
> +					   struct drm_clip_rect *clips,
> +					   unsigned num_clips)
> +{
> +	struct gm12u320_framebuffer *fb = to_gm12u320_fb(drm_fb);
> +	int x1, x2, y1, y2;
> +
> +	if (num_clips == 0)
> +		return 0;
> +
> +	x1 = clips->x1;
> +	x2 = clips->x2;
> +	y1 = clips->y1;
> +	y2 = clips->y2;
> +
> +	while (--num_clips) {
> +		clips++;
> +		x1 = min_t(int, x1, (int)clips->x1);
> +		x2 = max_t(int, x2, (int)clips->x2);
> +		y1 = min_t(int, y1, (int)clips->y1);
> +		y2 = max_t(int, y2, (int)clips->y2);
> +	}
> +
> +	gm12u320_fb_mark_dirty(fb, x1, x2, y1, y2);
> +
> +	return 0;
> +}
> +
> +static void gm12u320_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
> +{
> +	struct gm12u320_framebuffer *fb = to_gm12u320_fb(drm_fb);
> +
> +	if (fb->obj)
> +		drm_gem_object_unreference_unlocked(&fb->obj->base);
> +
> +	drm_framebuffer_cleanup(drm_fb);
> +	kfree(fb);
> +}
> +
> +static const struct drm_framebuffer_funcs gm12u320fb_funcs = {
> +	.destroy = gm12u320_user_framebuffer_destroy,
> +	.dirty = gm12u320_user_framebuffer_dirty,
> +};
> +
> +static int
> +gm12u320_framebuffer_init(struct drm_device *dev,
> +			  struct gm12u320_framebuffer *fb,
> +			  const struct drm_mode_fb_cmd2 *mode_cmd,
> +			  struct gm12u320_gem_object *obj)
> +{
> +	int ret;
> +
> +	fb->obj = obj;
> +	drm_helper_mode_fill_fb_struct(dev, &fb->base, mode_cmd);
> +	ret = drm_framebuffer_init(dev, &fb->base, &gm12u320fb_funcs);
> +	return ret;
> +}
> +
> +static int gm12u320fb_create(struct drm_fb_helper *helper,
> +			     struct drm_fb_helper_surface_size *sizes)
> +{
> +	struct gm12u320_fbdev *fbdev =
> +		container_of(helper, struct gm12u320_fbdev, helper);
> +	struct drm_device *dev = fbdev->helper.dev;
> +	struct fb_info *info;
> +	struct drm_framebuffer *drm_fb;
> +	struct drm_mode_fb_cmd2 mode_cmd;
> +	struct gm12u320_gem_object *obj;
> +	uint32_t size;
> +	int ret = 0;
> +
> +	if (sizes->surface_bpp == 24)
> +		sizes->surface_bpp = 32;
> +
> +	mode_cmd.width = sizes->surface_width;
> +	mode_cmd.height = sizes->surface_height;
> +	mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
> +
> +	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
> +							  sizes->surface_depth);
> +
> +	size = mode_cmd.pitches[0] * mode_cmd.height;
> +	size = ALIGN(size, PAGE_SIZE);
> +
> +	obj = gm12u320_gem_alloc_object(dev, size);
> +	if (!obj)
> +		goto out;
> +
> +	ret = gm12u320_gem_vmap(obj);
> +	if (ret) {
> +		DRM_ERROR("failed to vmap fb\n");
> +		goto out_gfree;
> +	}
> +
> +	info = drm_fb_helper_alloc_fbi(helper);
> +	if (IS_ERR(info)) {
> +		ret = PTR_ERR(info);
> +		goto out_gfree;
> +	}
> +	info->par = fbdev;
> +
> +	ret = gm12u320_framebuffer_init(dev, &fbdev->fb, &mode_cmd, obj);
> +	if (ret)
> +		goto out_gfree;
> +
> +	drm_fb = &fbdev->fb.base;
> +
> +	fbdev->helper.fb = drm_fb;
> +
> +	strcpy(info->fix.id, "gm12u320drmfb");
> +
> +	info->screen_base = fbdev->fb.obj->vmapping;
> +	info->fix.smem_len = size;
> +	info->fix.smem_start = (unsigned long)fbdev->fb.obj->vmapping;
> +
> +	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
> +	info->fbops = &gm12u320_fb_ops;
> +	drm_fb_helper_fill_fix(info, drm_fb->pitches[0], drm_fb->format->depth);
> +	drm_fb_helper_fill_var(info, &fbdev->helper,
> +			       sizes->fb_width, sizes->fb_height);
> +#ifdef CONFIG_DRM_FBDEV_EMULATION
> +	info->fbdefio = &gm12u320_fb_defio;
> +	fb_deferred_io_init(info);
> +#endif
> +
> +	DRM_DEBUG_KMS("allocated %dx%d vmal %p\n",
> +		      drm_fb->width, drm_fb->height,
> +		      fbdev->fb.obj->vmapping);
> +
> +	return ret;
> +
> +out_gfree:
> +	drm_gem_object_unreference_unlocked(&fbdev->fb.obj->base);
> +out:
> +	return ret;
> +}
> +
> +static const struct drm_fb_helper_funcs gm12u320_fb_helper_funcs = {
> +	.fb_probe = gm12u320fb_create,
> +};
> +
> +static void gm12u320_fbdev_destroy(struct drm_device *dev,
> +				   struct gm12u320_fbdev *fbdev)
> +{
> +#ifdef CONFIG_DRM_FBDEV_EMULATION
> +	fb_deferred_io_cleanup(fbdev->helper.fbdev);
> +#endif
> +	drm_fb_helper_unregister_fbi(&fbdev->helper);
> +	drm_fb_helper_fini(&fbdev->helper);
> +	drm_framebuffer_unregister_private(&fbdev->fb.base);
> +	drm_framebuffer_cleanup(&fbdev->fb.base);
> +	drm_gem_object_unreference_unlocked(&fbdev->fb.obj->base);
> +}
> +
> +int gm12u320_fbdev_init(struct drm_device *dev)
> +{
> +	struct gm12u320_device *gm12u320 = dev->dev_private;
> +	struct gm12u320_fbdev *fbdev;
> +	int ret;
> +
> +	fbdev = kzalloc(sizeof(struct gm12u320_fbdev), GFP_KERNEL);
> +	if (!fbdev)
> +		return -ENOMEM;
> +
> +	gm12u320->fbdev = fbdev;
> +
> +	drm_fb_helper_prepare(dev, &fbdev->helper, &gm12u320_fb_helper_funcs);
> +
> +	ret = drm_fb_helper_init(dev, &fbdev->helper, 1);
> +	if (ret)
> +		goto free;
> +
> +	ret = drm_fb_helper_single_add_all_connectors(&fbdev->helper);
> +	if (ret)
> +		goto fini;
> +
> +	/* disable all the possible outputs/crtcs before entering KMS mode */
> +	drm_helper_disable_unused_functions(dev);
> +
> +	ret = drm_fb_helper_initial_config(&fbdev->helper, 32);
> +	if (ret)
> +		goto fini;
> +
> +	return 0;
> +
> +fini:
> +	drm_fb_helper_fini(&fbdev->helper);
> +free:
> +	kfree(fbdev);
> +	return ret;
> +}
> +
> +void gm12u320_fbdev_cleanup(struct drm_device *dev)
> +{
> +	struct gm12u320_device *gm12u320 = dev->dev_private;
> +
> +	if (!gm12u320->fbdev)
> +		return;
> +
> +	gm12u320_fbdev_destroy(dev, gm12u320->fbdev);
> +	kfree(gm12u320->fbdev);
> +	gm12u320->fbdev = NULL;
> +}
> +
> +void gm12u320_fbdev_unplug(struct drm_device *dev)
> +{
> +	struct gm12u320_device *gm12u320 = dev->dev_private;
> +
> +	if (!gm12u320->fbdev)
> +		return;
> +
> +	drm_fb_helper_unlink_fbi(&gm12u320->fbdev->helper);
> +}
> +
> +struct drm_framebuffer *
> +gm12u320_fb_user_fb_create(struct drm_device *dev,
> +			   struct drm_file *file,
> +			   const struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> +	struct drm_gem_object *obj;
> +	struct gm12u320_framebuffer *fb;
> +	int ret;
> +	uint32_t size;
> +
> +	obj = drm_gem_object_lookup(file, mode_cmd->handles[0]);
> +	if (obj == NULL)
> +		return ERR_PTR(-ENOENT);
> +
> +	size = mode_cmd->pitches[0] * mode_cmd->height;
> +	size = ALIGN(size, PAGE_SIZE);
> +
> +	if (size > obj->size) {
> +		DRM_ERROR("object size not sufficient for fb %d %zu %d %d\n",
> +			  size, obj->size, mode_cmd->pitches[0],
> +			  mode_cmd->height);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
> +	if (fb == NULL)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ret = gm12u320_framebuffer_init(dev, fb, mode_cmd, to_gm12u320_bo(obj));
> +	if (ret) {
> +		kfree(fb);
> +		return ERR_PTR(-EINVAL);
> +	}
> +	return &fb->base;
> +}
> diff --git a/drivers/gpu/drm/gm12u320/gm12u320_gem.c b/drivers/gpu/drm/gm12u320/gm12u320_gem.c
> new file mode 100644
> index 000000000000..7bc576fae177
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/gm12u320_gem.c
> @@ -0,0 +1,237 @@
> +/*
> + * Copyright (C) 2012-2016 Red Hat Inc.
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License v2. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include "gm12u320_drv.h"
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +
> +struct gm12u320_gem_object *
> +gm12u320_gem_alloc_object(struct drm_device *dev, size_t size)
> +{
> +	struct gm12u320_gem_object *obj;
> +
> +	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
> +	if (obj == NULL)
> +		return NULL;
> +
> +	if (drm_gem_object_init(dev, &obj->base, size) != 0) {
> +		kfree(obj);
> +		return NULL;
> +	}
> +
> +	obj->flags = GM12U320_BO_CACHEABLE;
> +	return obj;
> +}
> +
> +static int gm12u320_gem_create(struct drm_file *file, struct drm_device *dev,
> +			       uint64_t size, uint32_t *handle_p)
> +{
> +	struct gm12u320_gem_object *obj;
> +	int ret;
> +	u32 handle;
> +
> +	size = roundup(size, PAGE_SIZE);
> +
> +	obj = gm12u320_gem_alloc_object(dev, size);
> +	if (obj == NULL)
> +		return -ENOMEM;
> +
> +	ret = drm_gem_handle_create(file, &obj->base, &handle);
> +	if (ret) {
> +		drm_gem_object_release(&obj->base);
> +		kfree(obj);
> +		return ret;
> +	}
> +
> +	drm_gem_object_unreference_unlocked(&obj->base);
> +	*handle_p = handle;
> +	return 0;
> +}
> +
> +static void update_vm_cache_attr(struct gm12u320_gem_object *obj,
> +				 struct vm_area_struct *vma)
> +{
> +	DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags);
> +
> +	/* non-cacheable as default. */
> +	if (obj->flags & GM12U320_BO_CACHEABLE) {
> +		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
> +	} else if (obj->flags & GM12U320_BO_WC) {
> +		vma->vm_page_prot =
> +			pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
> +	} else {
> +		vma->vm_page_prot =
> +			pgprot_noncached(vm_get_page_prot(vma->vm_flags));
> +	}
> +}
> +
> +int gm12u320_dumb_create(struct drm_file *file, struct drm_device *dev,
> +			 struct drm_mode_create_dumb *args)
> +{
> +	args->pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
> +	args->size = args->pitch * args->height;
> +	return gm12u320_gem_create(file, dev,
> +			      args->size, &args->handle);
> +}
> +
> +int gm12u320_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +	int ret;
> +
> +	ret = drm_gem_mmap(filp, vma);
> +	if (ret)
> +		return ret;
> +
> +	vma->vm_flags &= ~VM_PFNMAP;
> +	vma->vm_flags |= VM_MIXEDMAP;
> +
> +	update_vm_cache_attr(to_gm12u320_bo(vma->vm_private_data), vma);
> +
> +	return ret;
> +}
> +
> +int gm12u320_gem_fault(struct vm_fault *vmf)
> +{
> +	struct vm_area_struct *vma = vmf->vma;
> +	struct gm12u320_gem_object *obj = to_gm12u320_bo(vma->vm_private_data);
> +	unsigned int page_offset;
> +	struct page *page;
> +	int ret = 0;
> +
> +	page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
> +
> +	if (!obj->pages)
> +		return VM_FAULT_SIGBUS;
> +
> +	page = obj->pages[page_offset];
> +	ret = vm_insert_page(vma, vmf->address, page);
> +	switch (ret) {
> +	case -EAGAIN:
> +	case 0:
> +	case -ERESTARTSYS:
> +		return VM_FAULT_NOPAGE;
> +	case -ENOMEM:
> +		return VM_FAULT_OOM;
> +	default:
> +		return VM_FAULT_SIGBUS;
> +	}
> +}

Same plea for some standard mmap shmem-gem helpers as for prime helpers?

> +int gm12u320_gem_get_pages(struct gm12u320_gem_object *obj)
> +{
> +	struct page **pages;
> +
> +	if (obj->pages)
> +		return 0;
> +
> +	pages = drm_gem_get_pages(&obj->base);
> +	if (IS_ERR(pages))
> +		return PTR_ERR(pages);
> +
> +	obj->pages = pages;
> +
> +	return 0;
> +}
> +
> +void gm12u320_gem_put_pages(struct gm12u320_gem_object *obj)
> +{
> +	if (obj->base.import_attach) {
> +		drm_free_large(obj->pages);
> +		obj->pages = NULL;
> +		return;
> +	}
> +
> +	drm_gem_put_pages(&obj->base, obj->pages, false, false);
> +	obj->pages = NULL;
> +}
> +
> +int gm12u320_gem_vmap(struct gm12u320_gem_object *obj)
> +{
> +	int page_count = obj->base.size / PAGE_SIZE;
> +	int ret;
> +
> +	if (obj->base.import_attach) {
> +		obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf);
> +		if (!obj->vmapping)
> +			return -ENOMEM;
> +		return 0;
> +	}
> +
> +	ret = gm12u320_gem_get_pages(obj);
> +	if (ret)
> +		return ret;
> +
> +	obj->vmapping = vmap(obj->pages, page_count, 0, PAGE_KERNEL);
> +	if (!obj->vmapping)
> +		return -ENOMEM;
> +	return 0;
> +}
> +
> +void gm12u320_gem_vunmap(struct gm12u320_gem_object *obj)
> +{
> +	if (obj->base.import_attach) {
> +		dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping);
> +		return;
> +	}
> +
> +	vunmap(obj->vmapping);
> +
> +	gm12u320_gem_put_pages(obj);
> +}
> +
> +void gm12u320_gem_free_object(struct drm_gem_object *gem_obj)
> +{
> +	struct gm12u320_gem_object *obj = to_gm12u320_bo(gem_obj);
> +
> +	if (obj->vmapping)
> +		gm12u320_gem_vunmap(obj);
> +
> +	if (gem_obj->import_attach) {
> +		drm_prime_gem_destroy(gem_obj, obj->sg);
> +		put_device(gem_obj->dev->dev);
> +	}
> +
> +	if (obj->pages)
> +		gm12u320_gem_put_pages(obj);
> +
> +	drm_gem_free_mmap_offset(gem_obj);
> +}
> +
> +/* the dumb interface doesn't work with the GEM straight MMAP
> +   interface, it expects to do MMAP on the drm fd, like normal */
> +int gm12u320_gem_mmap(struct drm_file *file, struct drm_device *dev,
> +		      uint32_t handle, uint64_t *offset)
> +{
> +	struct gm12u320_gem_object *gobj;
> +	struct drm_gem_object *obj;
> +	int ret = 0;
> +
> +	mutex_lock(&dev->struct_mutex);
> +	obj = drm_gem_object_lookup(file, handle);
> +	if (obj == NULL) {
> +		ret = -ENOENT;
> +		goto unlock;
> +	}
> +	gobj = to_gm12u320_bo(obj);
> +
> +	ret = gm12u320_gem_get_pages(gobj);
> +	if (ret)
> +		goto out;
> +	ret = drm_gem_create_mmap_offset(obj);
> +	if (ret)
> +		goto out;
> +
> +	*offset = drm_vma_node_offset_addr(&gobj->base.vma_node);
> +
> +out:
> +	drm_gem_object_unreference(&gobj->base);
> +unlock:
> +	mutex_unlock(&dev->struct_mutex);
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/gm12u320/gm12u320_main.c b/drivers/gpu/drm/gm12u320/gm12u320_main.c
> new file mode 100644
> index 000000000000..6b9ab9d6d15b
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/gm12u320_main.c
> @@ -0,0 +1,506 @@
> +/*
> + * Copyright (C) 2012-2016 Red Hat Inc.
> + *
> + * Based in parts on the udl code. Based in parts on the gm12u320 fb driver:
> + * Copyright (C) 2013 Viacheslav Nurmekhamitov <slavrn at yandex.ru>
> + * Copyright (C) 2009 Roberto De Ioris <roberto at unbit.it>
> + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml at gmail.com>
> + * Copyright (C) 2009 Bernie Thompson <bernie at plugable.com>
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License v2. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +#include <drm/drmP.h>
> +#include <linux/dma-buf.h>
> +#include "gm12u320_drv.h"
> +
> +static bool eco_mode;
> +module_param(eco_mode, bool, 0644);
> +MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)");
> +
> +#define MISC_RCV_EPT			1
> +#define DATA_RCV_EPT			2
> +#define DATA_SND_EPT			3
> +#define MISC_SND_EPT			4
> +
> +#define DATA_BLOCK_HEADER_SIZE		84
> +#define DATA_BLOCK_CONTENT_SIZE		64512
> +#define DATA_BLOCK_FOOTER_SIZE		20
> +#define DATA_BLOCK_SIZE			(DATA_BLOCK_HEADER_SIZE + \
> +					 DATA_BLOCK_CONTENT_SIZE + \
> +					 DATA_BLOCK_FOOTER_SIZE)
> +#define DATA_LAST_BLOCK_CONTENT_SIZE	4032
> +#define DATA_LAST_BLOCK_SIZE		(DATA_BLOCK_HEADER_SIZE + \
> +					 DATA_LAST_BLOCK_CONTENT_SIZE + \
> +					 DATA_BLOCK_FOOTER_SIZE)
> +
> +#define CMD_SIZE			31
> +#define READ_STATUS_SIZE		13
> +#define MISC_VALUE_SIZE			4
> +
> +#define CMD_TIMEOUT			200
> +#define DATA_TIMEOUT			1000
> +#define IDLE_TIMEOUT			2000
> +#define FIRST_FRAME_TIMEOUT		2000
> +
> +#define MISC_REQ_GET_SET_ECO_A		0xff
> +#define MISC_REQ_GET_SET_ECO_B		0x35
> +/* Windows driver does once evert second, with with arg d = 1, others 0 */
> +#define MISC_REQ_UNKNOWN1_A		0xff
> +#define MISC_REQ_UNKNOWN1_B		0x38
> +/* Windows driver does this on init, with arg a, b = 0, c = 0xa0, d = 4 */
> +#define MISC_REQ_UNKNOWN2_A		0xa5
> +#define MISC_REQ_UNKNOWN2_B		0x00
> +
> +static const char cmd_data[CMD_SIZE] = {
> +	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
> +	0x68, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff,
> +	0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x80, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> +};
> +
> +static const char cmd_draw[CMD_SIZE] = {
> +	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe,
> +	0x00, 0x00, 0x00, 0xc0, 0xd1, 0x05, 0x00, 0x40,
> +	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
> +};
> +
> +static const char cmd_misc[CMD_SIZE] = {
> +	0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
> +	0x04, 0x00, 0x00, 0x00, 0x80, 0x01, 0x10, 0xfd,
> +	0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
> +};
> +
> +static const char data_block_header[DATA_BLOCK_HEADER_SIZE] = {
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x04, 0x15, 0x00, 0x00, 0xfc, 0x00, 0x00,
> +	0x01, 0x00, 0x00, 0xdb
> +};
> +
> +static const char data_last_block_header[DATA_BLOCK_HEADER_SIZE] = {
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x2a, 0x00, 0x20, 0x00, 0xc0, 0x0f, 0x00, 0x00,
> +	0x01, 0x00, 0x00, 0xd7
> +};
> +
> +static const char data_block_footer[DATA_BLOCK_FOOTER_SIZE] = {
> +	0xfb, 0x14, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
> +	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	0x80, 0x00, 0x00, 0x4f
> +};
> +
> +static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320)
> +{
> +	int i, block_size;
> +	const char *hdr;
> +
> +	gm12u320->cmd_buf = kmalloc(CMD_SIZE, GFP_KERNEL);
> +	if (!gm12u320->cmd_buf)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < GM12U320_BLOCK_COUNT; i++) {
> +		if (i == GM12U320_BLOCK_COUNT - 1) {
> +			block_size = DATA_LAST_BLOCK_SIZE;
> +			hdr = data_last_block_header;
> +		} else {
> +			block_size = DATA_BLOCK_SIZE;
> +			hdr = data_block_header;
> +		}
> +
> +		gm12u320->data_buf[i] = kzalloc(block_size, GFP_KERNEL);
> +		if (!gm12u320->data_buf[i])
> +			return -ENOMEM;
> +
> +		memcpy(gm12u320->data_buf[i], hdr, DATA_BLOCK_HEADER_SIZE);
> +		memcpy(gm12u320->data_buf[i] +
> +				(block_size - DATA_BLOCK_FOOTER_SIZE),
> +		       data_block_footer, DATA_BLOCK_FOOTER_SIZE);
> +	}
> +
> +	return 0;
> +}
> +
> +static void gm12u320_usb_free(struct gm12u320_device *gm12u320)
> +{
> +	int i;
> +
> +	for (i = 0; i < GM12U320_BLOCK_COUNT; i++)
> +		kfree(gm12u320->data_buf[i]);
> +
> +	kfree(gm12u320->cmd_buf);
> +}
> +
> +static int gm12u320_misc_request(struct gm12u320_device *gm12u320,
> +				 u8 req_a, u8 req_b,
> +				 u8 arg_a, u8 arg_b, u8 arg_c, u8 arg_d)
> +{
> +	int ret, len;
> +	u8 *buf, val;
> +
> +	buf = kmalloc(CMD_SIZE, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	memcpy(buf, &cmd_misc, CMD_SIZE);
> +	buf[20] = req_a;
> +	buf[21] = req_b;
> +	buf[22] = arg_a;
> +	buf[23] = arg_b;
> +	buf[24] = arg_c;
> +	buf[25] = arg_d;
> +
> +	/* Send request */
> +	ret = usb_bulk_msg(gm12u320->udev,
> +			   usb_sndbulkpipe(gm12u320->udev, MISC_SND_EPT),
> +			   buf, CMD_SIZE, &len, CMD_TIMEOUT);
> +	if (ret || len != CMD_SIZE) {
> +		dev_err(&gm12u320->udev->dev, "Misc. req. error %d\n", ret);
> +		ret = -EIO;
> +		goto leave;
> +	}
> +
> +	/* Read value */
> +	ret = usb_bulk_msg(gm12u320->udev,
> +			   usb_rcvbulkpipe(gm12u320->udev, MISC_RCV_EPT),
> +			   buf, MISC_VALUE_SIZE, &len, DATA_TIMEOUT);
> +	if (ret || len != MISC_VALUE_SIZE) {
> +		dev_err(&gm12u320->udev->dev, "Misc. value error %d\n", ret);
> +		ret = -EIO;
> +		goto leave;
> +	}
> +	val = buf[0];
> +
> +	/* Read status */
> +	ret = usb_bulk_msg(gm12u320->udev,
> +			   usb_rcvbulkpipe(gm12u320->udev, MISC_RCV_EPT),
> +			   buf, READ_STATUS_SIZE, &len, CMD_TIMEOUT);
> +	if (ret || len != READ_STATUS_SIZE) {
> +		dev_err(&gm12u320->udev->dev, "Misc. status error %d\n", ret);
> +		ret = -EIO;
> +		goto leave;
> +	}
> +
> +	ret = val;
> +leave:
> +	kfree(buf);
> +	return ret;
> +}
> +
> +void gm12u320_32bpp_to_24bpp_packed(u8 *dst, u8 *src, int len)
> +{
> +	while (len--) {
> +		*dst++ = *src++;
> +		*dst++ = *src++;
> +		*dst++ = *src++;
> +		src++;
> +	}
> +}
> +
> +static void gm12u320_copy_fb_to_blocks(struct gm12u320_framebuffer *fb,
> +				       int x1, int x2, int y1, int y2)
> +{
> +	struct drm_device *dev = fb->base.dev;
> +	struct gm12u320_device *gm12u320 = dev->dev_private;
> +	int block, dst_offset, len, remain, ret;
> +	u8 *src;
> +
> +	if (fb->obj->base.import_attach) {
> +		ret = dma_buf_begin_cpu_access(
> +			fb->obj->base.import_attach->dmabuf, DMA_FROM_DEVICE);
> +		if (ret) {
> +			DRM_ERROR("dma_buf_begin_cpu_access err: %d\n", ret);
> +			return;
> +		}
> +	}
> +
> +	if (!fb->obj->vmapping) {
> +		ret = gm12u320_gem_vmap(fb->obj);
> +		if (ret) {
> +			DRM_ERROR("failed to vmap fb: %d\n", ret);
> +			goto end_cpu_access;
> +		}
> +	}
> +
> +	src = fb->obj->vmapping + y1 * fb->base.pitches[0] + x1 * 4;
> +
> +	x1 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
> +	x2 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
> +
> +	for (; y1 < y2; y1++) {
> +		remain = 0;
> +		len = (x2 - x1) * 3;
> +		dst_offset = (y1 * GM12U320_REAL_WIDTH + x1) * 3;
> +		block = dst_offset / DATA_BLOCK_CONTENT_SIZE;
> +		dst_offset %= DATA_BLOCK_CONTENT_SIZE;
> +
> +		if ((dst_offset + len) > DATA_BLOCK_CONTENT_SIZE) {
> +			remain = dst_offset + len - DATA_BLOCK_CONTENT_SIZE;
> +			len = DATA_BLOCK_CONTENT_SIZE - dst_offset;
> +		}
> +
> +		dst_offset += DATA_BLOCK_HEADER_SIZE;
> +		len /= 3;
> +
> +		gm12u320_32bpp_to_24bpp_packed(
> +			gm12u320->data_buf[block] + dst_offset,
> +			src, len);
> +
> +		if (remain) {
> +			block++;
> +			dst_offset = DATA_BLOCK_HEADER_SIZE;
> +			gm12u320_32bpp_to_24bpp_packed(
> +				gm12u320->data_buf[block] + dst_offset,
> +				src + len * 4, remain / 3);
> +		}
> +		src += fb->base.pitches[0];
> +	}
> +
> +end_cpu_access:
> +	if (fb->obj->base.import_attach) {
> +		ret = dma_buf_end_cpu_access(
> +			fb->obj->base.import_attach->dmabuf, DMA_FROM_DEVICE);
> +		if (ret)
> +			DRM_ERROR("dma_buf_end_cpu_access err: %d\n", ret);
> +	}
> +}
> +
> +static int gm12u320_fb_update_ready(struct gm12u320_device *gm12u320)
> +{
> +	int ret;
> +
> +	mutex_lock(&gm12u320->fb_update.lock);
> +	ret = !gm12u320->fb_update.run || gm12u320->fb_update.fb != NULL;
> +	mutex_unlock(&gm12u320->fb_update.lock);
> +
> +	return ret;
> +}
> +
> +static void gm12u320_fb_update_work(struct work_struct *work)
> +{
> +	struct gm12u320_device *gm12u320 =
> +		container_of(work, struct gm12u320_device, fb_update.work);
> +	int draw_status_timeout = FIRST_FRAME_TIMEOUT;
> +	int block, block_size, len, x1, x2, y1, y2;
> +	struct gm12u320_framebuffer *fb;
> +	int frame = 0;
> +	int ret = 0;
> +
> +	while (gm12u320->fb_update.run) {
> +		mutex_lock(&gm12u320->fb_update.lock);
> +		fb = gm12u320->fb_update.fb;
> +		x1 = gm12u320->fb_update.x1;
> +		x2 = gm12u320->fb_update.x2;
> +		y1 = gm12u320->fb_update.y1;
> +		y2 = gm12u320->fb_update.y2;
> +		gm12u320->fb_update.fb = NULL;
> +		mutex_unlock(&gm12u320->fb_update.lock);
> +
> +		if (fb) {
> +			gm12u320_copy_fb_to_blocks(fb, x1, x2, y1, y2);
> +			drm_framebuffer_unreference(&fb->base);
> +		}
> +
> +		for (block = 0; block < GM12U320_BLOCK_COUNT; block++) {
> +			if (block == GM12U320_BLOCK_COUNT - 1)
> +				block_size = DATA_LAST_BLOCK_SIZE;
> +			else
> +				block_size = DATA_BLOCK_SIZE;
> +
> +			/* Send data command to device */
> +			memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE);
> +			gm12u320->cmd_buf[8] = block_size & 0xff;
> +			gm12u320->cmd_buf[9] = block_size >> 8;
> +			gm12u320->cmd_buf[20] = 0xfc - block * 4;
> +			gm12u320->cmd_buf[21] = block | (frame << 7);
> +
> +			ret = usb_bulk_msg(gm12u320->udev,
> +				usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
> +				gm12u320->cmd_buf, CMD_SIZE, &len,
> +				CMD_TIMEOUT);
> +			if (ret || len != CMD_SIZE)
> +				goto err;
> +
> +			/* Send data block to device */
> +			ret = usb_bulk_msg(gm12u320->udev,
> +				usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
> +				gm12u320->data_buf[block], block_size,
> +				&len, DATA_TIMEOUT);
> +			if (ret || len != block_size)
> +				goto err;
> +
> +			/* Read status */
> +			ret = usb_bulk_msg(gm12u320->udev,
> +				usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
> +				gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
> +				CMD_TIMEOUT);
> +			if (ret || len != READ_STATUS_SIZE)
> +				goto err;
> +		}
> +
> +		/* Send draw command to device */
> +		memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE);
> +		ret = usb_bulk_msg(gm12u320->udev,
> +			usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT),
> +			gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
> +		if (ret || len != CMD_SIZE)
> +			goto err;
> +
> +		/* Read status */
> +		ret = usb_bulk_msg(gm12u320->udev,
> +			usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT),
> +			gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
> +			draw_status_timeout);
> +		if (ret || len != READ_STATUS_SIZE)
> +			goto err;
> +
> +		draw_status_timeout = CMD_TIMEOUT;
> +		frame = !frame;
> +
> +		/*
> +		 * We must draw a frame every 2s otherwise the projector
> +		 * switches back to showing its logo.
> +		 */
> +		wait_event_timeout(gm12u320->fb_update.waitq,
> +				   gm12u320_fb_update_ready(gm12u320),
> +				   msecs_to_jiffies(IDLE_TIMEOUT));
> +	}
> +	return;
> +err:
> +	/* Do not log errors caused by module unload or device unplug */
> +	if (ret != -ECONNRESET && ret != -ESHUTDOWN)
> +		dev_err(&gm12u320->udev->dev, "Frame update error: %d\n", ret);
> +}
> +
> +void gm12u320_start_fb_update(struct drm_device *dev)
> +{
> +	struct gm12u320_device *gm12u320 = dev->dev_private;
> +
> +	mutex_lock(&gm12u320->fb_update.lock);
> +	gm12u320->fb_update.run = true;
> +	mutex_unlock(&gm12u320->fb_update.lock);
> +
> +	queue_work(gm12u320->fb_update.workq, &gm12u320->fb_update.work);
> +}
> +
> +void gm12u320_stop_fb_update(struct drm_device *dev)
> +{
> +	struct gm12u320_device *gm12u320 = dev->dev_private;
> +
> +	mutex_lock(&gm12u320->fb_update.lock);
> +	gm12u320->fb_update.run = false;
> +	mutex_unlock(&gm12u320->fb_update.lock);
> +
> +	wake_up(&gm12u320->fb_update.waitq);
> +	cancel_work_sync(&gm12u320->fb_update.work);
> +
> +	mutex_lock(&gm12u320->fb_update.lock);
> +	if (gm12u320->fb_update.fb) {
> +		drm_framebuffer_unreference(&gm12u320->fb_update.fb->base);
> +		gm12u320->fb_update.fb = NULL;
> +	}
> +	mutex_unlock(&gm12u320->fb_update.lock);
> +}
> +
> +int gm12u320_driver_load(struct drm_device *dev, unsigned long flags)
> +{
> +	struct usb_device *udev = (void *)flags;
> +	struct gm12u320_device *gm12u320;
> +	int ret = -ENOMEM;
> +
> +	DRM_DEBUG("\n");
> +	gm12u320 = kzalloc(sizeof(struct gm12u320_device), GFP_KERNEL);
> +	if (!gm12u320)
> +		return -ENOMEM;
> +
> +	gm12u320->udev = udev;
> +	gm12u320->ddev = dev;
> +	dev->dev_private = gm12u320;
> +
> +	INIT_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work);
> +	mutex_init(&gm12u320->fb_update.lock);
> +	init_waitqueue_head(&gm12u320->fb_update.waitq);
> +
> +	ret = gm12u320_set_ecomode(dev);
> +	if (ret)
> +		goto err;
> +
> +	gm12u320->fb_update.workq = create_singlethread_workqueue(DRIVER_NAME);
> +	if (!gm12u320->fb_update.workq) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	ret = gm12u320_usb_alloc(gm12u320);
> +	if (ret)
> +		goto err_wq;
> +
> +	DRM_DEBUG("\n");
> +	ret = gm12u320_modeset_init(dev);
> +	if (ret)
> +		goto err_usb;
> +
> +	ret = gm12u320_fbdev_init(dev);
> +	if (ret)
> +		goto err_modeset;
> +
> +	ret = drm_vblank_init(dev, 1);
> +	if (ret)
> +		goto err_fb;
> +
> +	gm12u320_start_fb_update(dev);
> +
> +	return 0;
> +
> +err_fb:
> +	gm12u320_fbdev_cleanup(dev);
> +err_modeset:
> +	gm12u320_modeset_cleanup(dev);
> +err_usb:
> +	gm12u320_usb_free(gm12u320);
> +err_wq:
> +	destroy_workqueue(gm12u320->fb_update.workq);
> +err:
> +	kfree(gm12u320);
> +	DRM_ERROR("%d\n", ret);
> +	return ret;
> +}
> +
> +void gm12u320_driver_unload(struct drm_device *dev)
> +{
> +	struct gm12u320_device *gm12u320 = dev->dev_private;
> +
> +	drm_vblank_cleanup(dev);
> +	gm12u320_fbdev_cleanup(dev);
> +	gm12u320_modeset_cleanup(dev);
> +	gm12u320_usb_free(gm12u320);
> +	destroy_workqueue(gm12u320->fb_update.workq);
> +	kfree(gm12u320);
> +}
> +
> +int gm12u320_set_ecomode(struct drm_device *dev)
> +{
> +	struct gm12u320_device *gm12u320 = dev->dev_private;
> +
> +	return gm12u320_misc_request(gm12u320, MISC_REQ_GET_SET_ECO_A,
> +				     MISC_REQ_GET_SET_ECO_B, 0x01 /* set */,
> +				     eco_mode ? 0x01 : 0x00, 0x00, 0x01);
> +}
> diff --git a/drivers/gpu/drm/gm12u320/gm12u320_modeset.c b/drivers/gpu/drm/gm12u320/gm12u320_modeset.c
> new file mode 100644
> index 000000000000..bb3122295d15
> --- /dev/null
> +++ b/drivers/gpu/drm/gm12u320/gm12u320_modeset.c
> @@ -0,0 +1,143 @@
> +/*
> + * Copyright (C) 2012-2016 Red Hat Inc.
> + *
> + * Based in parts on the udl code. Based in parts on the gm12u320 fb driver:
> + * Copyright (C) 2013 Viacheslav Nurmekhamitov <slavrn at yandex.ru>
> + * Copyright (C) 2009 Roberto De Ioris <roberto at unbit.it>
> + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml at gmail.com>
> + * Copyright (C) 2009 Bernie Thompson <bernie at plugable.com>
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License v2. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_plane_helper.h>
> +#include "gm12u320_drv.h"
> +
> +static void gm12u320_crtc_dpms(struct drm_crtc *crtc, int mode)
> +{
> +}
> +
> +static int gm12u320_crtc_mode_set(struct drm_crtc *crtc,
> +				  struct drm_display_mode *mode,
> +				  struct drm_display_mode *adjusted_mode,
> +				  int x, int y,
> +				  struct drm_framebuffer *old_fb)
> +
> +{
> +	struct gm12u320_framebuffer *fb = to_gm12u320_fb(crtc->primary->fb);
> +
> +	gm12u320_fb_mark_dirty(fb, 0, GM12U320_USER_WIDTH, 0, GM12U320_HEIGHT);
> +	return 0;
> +}
> +
> +
> +static void gm12u320_crtc_disable(struct drm_crtc *crtc)
> +{
> +}
> +
> +static void gm12u320_crtc_destroy(struct drm_crtc *crtc)
> +{
> +	drm_crtc_cleanup(crtc);
> +	kfree(crtc);
> +}
> +
> +static int gm12u320_crtc_page_flip(struct drm_crtc *crtc,
> +				   struct drm_framebuffer *drm_fb,
> +				   struct drm_pending_vblank_event *event,
> +				   uint32_t page_flip_flags,
> +				   struct drm_modeset_acquire_ctx *ctx)
> +{
> +	struct gm12u320_framebuffer *fb = to_gm12u320_fb(drm_fb);
> +	struct drm_device *dev = crtc->dev;
> +	unsigned long flags;
> +
> +	gm12u320_fb_mark_dirty(fb, 0, GM12U320_USER_WIDTH, 0, GM12U320_HEIGHT);
> +
> +	spin_lock_irqsave(&dev->event_lock, flags);
> +	if (event)
> +		drm_crtc_send_vblank_event(crtc, event);
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +
> +	crtc->primary->fb = drm_fb;
> +
> +	return 0;
> +}
> +
> +static void gm12u320_crtc_prepare(struct drm_crtc *crtc)
> +{
> +}
> +
> +static void gm12u320_crtc_commit(struct drm_crtc *crtc)
> +{
> +	gm12u320_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
> +}
> +
> +static const struct drm_crtc_helper_funcs gm12u320_helper_funcs = {
> +	.dpms = gm12u320_crtc_dpms,
> +	.mode_set = gm12u320_crtc_mode_set,
> +	.prepare = gm12u320_crtc_prepare,
> +	.commit = gm12u320_crtc_commit,
> +	.disable = gm12u320_crtc_disable,
> +};
> +
> +static const struct drm_crtc_funcs gm12u320_crtc_funcs = {
> +	.set_config = drm_crtc_helper_set_config,
> +	.destroy = gm12u320_crtc_destroy,
> +	.page_flip = gm12u320_crtc_page_flip,
> +};
> +
> +static int gm12u320_crtc_init(struct drm_device *dev)
> +{
> +	struct drm_crtc *crtc;
> +
> +	crtc = kzalloc(sizeof(struct drm_crtc) +
> +		       sizeof(struct drm_connector *), GFP_KERNEL);
> +	if (crtc == NULL)
> +		return -ENOMEM;
> +
> +	drm_crtc_init(dev, crtc, &gm12u320_crtc_funcs);
> +	drm_crtc_helper_add(crtc, &gm12u320_helper_funcs);
> +
> +	return 0;
> +}
> +
> +static const struct drm_mode_config_funcs gm12u320_mode_funcs = {
> +	.fb_create = gm12u320_fb_user_fb_create,
> +	.output_poll_changed = NULL,
> +};

Submitting a non-atomic driver for inclusions in todays time! tsk ...

Also, this is a prime example of hw that wants to use the simple display
pipe helpers, which gives you atomic and all the new gizmos for free.

> +
> +int gm12u320_modeset_init(struct drm_device *dev)
> +{
> +	struct drm_encoder *encoder;
> +
> +	drm_mode_config_init(dev);
> +
> +	dev->mode_config.min_width = GM12U320_USER_WIDTH;
> +	dev->mode_config.min_height = GM12U320_HEIGHT;
> +
> +	dev->mode_config.max_width = 2048;
> +	dev->mode_config.max_height = 2048;
> +
> +	dev->mode_config.prefer_shadow = 0;
> +	dev->mode_config.preferred_depth = 24;
> +
> +	dev->mode_config.funcs = &gm12u320_mode_funcs;
> +
> +	gm12u320_crtc_init(dev);
> +
> +	encoder = gm12u320_encoder_init(dev);
> +
> +	gm12u320_connector_init(dev, encoder);
> +
> +	return 0;
> +}
> +
> +void gm12u320_modeset_cleanup(struct drm_device *dev)
> +{
> +	drm_mode_config_cleanup(dev);
> +}
> -- 
> 2.13.0
> 
> _______________________________________________
> 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