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@xxxxxxxxx>. > > Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx> 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@xxxxxxxxxx> > +L: dri-devel@xxxxxxxxxxxxxxxxxxxxx > +S: Supported > +F: drivers/gpu/drm/gm12u320/ > + > DRM DRIVERS FOR NVIDIA TEGRA > M: Thierry Reding <thierry.reding@xxxxxxxxx> > L: dri-devel@xxxxxxxxxxxxxxxxxxxxx > 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@xxxxxxxxx> > + * Copyright (C) 2009 Roberto De Ioris <roberto@xxxxxxxx> > + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@xxxxxxxxx> > + * Copyright (C) 2009 Bernie Thompson <bernie@xxxxxxxxxxxx> > + * > + * 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@xxxxxxxxxx>"); > +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@xxxxxxxxx> > + * Copyright (C) 2009 Roberto De Ioris <roberto@xxxxxxxx> > + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@xxxxxxxxx> > + * Copyright (C) 2009 Bernie Thompson <bernie@xxxxxxxxxxxx> > + * > + * 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@xxxxxxxxx> > + * Copyright (C) 2009 Roberto De Ioris <roberto@xxxxxxxx> > + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@xxxxxxxxx> > + * Copyright (C) 2009 Bernie Thompson <bernie@xxxxxxxxxxxx> > + * > + * 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@xxxxxxxxx> > + * Copyright (C) 2009 Roberto De Ioris <roberto@xxxxxxxx> > + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@xxxxxxxxx> > + * Copyright (C) 2009 Bernie Thompson <bernie@xxxxxxxxxxxx> > + * > + * 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@xxxxxxxxx> > + * Copyright (C) 2009 Roberto De Ioris <roberto@xxxxxxxx> > + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@xxxxxxxxx> > + * Copyright (C) 2009 Bernie Thompson <bernie@xxxxxxxxxxxx> > + * > + * 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@xxxxxxxxx> > + * Copyright (C) 2009 Roberto De Ioris <roberto@xxxxxxxx> > + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@xxxxxxxxx> > + * Copyright (C) 2009 Bernie Thompson <bernie@xxxxxxxxxxxx> > + * > + * 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@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/dri-devel -- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel