From patchwork Fri Jun 3 13:06:32 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 9153045 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id A0E9A60221 for ; Fri, 3 Jun 2016 13:06:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8C2DD26E39 for ; Fri, 3 Jun 2016 13:06:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 800F128304; Fri, 3 Jun 2016 13:06:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 59AC226E39 for ; Fri, 3 Jun 2016 13:06:42 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DF83C6ED9D; Fri, 3 Jun 2016 13:06:40 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by gabe.freedesktop.org (Postfix) with ESMTPS id 2601D6ED9D for ; Fri, 3 Jun 2016 13:06:39 +0000 (UTC) Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D5058C04B32D for ; Fri, 3 Jun 2016 13:06:38 +0000 (UTC) Received: from shalem.localdomain.com (vpn1-4-176.ams2.redhat.com [10.36.4.176]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u53D6Y1c022961; Fri, 3 Jun 2016 09:06:36 -0400 From: Hans de Goede To: Dave Airlie Subject: [PATCH v2] drm: Add Grain Media GM12U320 kms driver Date: Fri, 3 Jun 2016 15:06:32 +0200 Message-Id: <1464959192-21064-1-git-send-email-hdegoede@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.26 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.31]); Fri, 03 Jun 2016 13:06:38 +0000 (UTC) Cc: Hans de Goede , dri-devel@lists.freedesktop.org X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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 . Signed-off-by: Hans de Goede --- Changes in v2: -Rebase on 4.7-rc1 -Sync with udl, bring in any fixes done to udl since v1 --- MAINTAINERS | 6 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/gm12u320/Kconfig | 16 + drivers/gpu/drm/gm12u320/Makefile | 7 + drivers/gpu/drm/gm12u320/gm12u320_connector.c | 151 ++++++++ drivers/gpu/drm/gm12u320/gm12u320_dmabuf.c | 283 ++++++++++++++ drivers/gpu/drm/gm12u320/gm12u320_drv.c | 160 ++++++++ drivers/gpu/drm/gm12u320/gm12u320_drv.h | 129 +++++++ drivers/gpu/drm/gm12u320/gm12u320_encoder.c | 75 ++++ drivers/gpu/drm/gm12u320/gm12u320_fb.c | 345 ++++++++++++++++++ drivers/gpu/drm/gm12u320/gm12u320_gem.c | 237 ++++++++++++ drivers/gpu/drm/gm12u320/gm12u320_main.c | 507 ++++++++++++++++++++++++++ drivers/gpu/drm/gm12u320/gm12u320_modeset.c | 144 ++++++++ 14 files changed, 2063 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 216d447..5c05af2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3904,6 +3904,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 +L: dri-devel@lists.freedesktop.org +S: Supported +F: drivers/gpu/drm/gm12u320/ + DRM DRIVERS FOR NVIDIA TEGRA M: Thierry Reding L: dri-devel@lists.freedesktop.org diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index fc35731..1380e4a 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -290,3 +290,5 @@ source "drivers/gpu/drm/arc/Kconfig" source "drivers/gpu/drm/hisilicon/Kconfig" source "drivers/gpu/drm/mediatek/Kconfig" + +source "drivers/gpu/drm/gm12u320/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index be43afb..2e06396 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-$(CONFIG_DRM_STI) += sti/ obj-$(CONFIG_DRM_IMX) += imx/ obj-$(CONFIG_DRM_MEDIATEK) += mediatek/ +obj-$(CONFIG_DRM_GM12U320) += gm12u320/ obj-y += i2c/ obj-y += panel/ obj-y += bridge/ diff --git a/drivers/gpu/drm/gm12u320/Kconfig b/drivers/gpu/drm/gm12u320/Kconfig new file mode 100644 index 0000000..cfecaac --- /dev/null +++ b/drivers/gpu/drm/gm12u320/Kconfig @@ -0,0 +1,16 @@ +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 FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_DEFERRED_IO + select DRM_KMS_HELPER + select DRM_KMS_FB_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 0000000..6234473 --- /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 0000000..1a31915 --- /dev/null +++ b/drivers/gpu/drm/gm12u320/gm12u320_connector.c @@ -0,0 +1,151 @@ +/* + * 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 + * Copyright (C) 2009 Roberto De Ioris + * Copyright (C) 2009 Jaya Kumar + * Copyright (C) 2009 Bernie Thompson + * + * 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 +#include +#include +#include +#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); + + drm_object_attach_property(&connector->base, + dev->mode_config.dirty_info_property, 1); + + 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 0000000..c15ad4b --- /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 . + */ + +#include +#include "gm12u320_drv.h" +#include +#include + +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; +} + +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, + .kmap = gm12u320_dmabuf_kmap, + .kmap_atomic = gm12u320_dmabuf_kmap_atomic, + .kunmap = gm12u320_dmabuf_kunmap, + .kunmap_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 dma_buf_export(&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); +} diff --git a/drivers/gpu/drm/gm12u320/gm12u320_drv.c b/drivers/gpu/drm/gm12u320/gm12u320_drv.c new file mode 100644 index 0000000..eff3a44 --- /dev/null +++ b/drivers/gpu/drm/gm12u320/gm12u320_drv.c @@ -0,0 +1,160 @@ +/* + * 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 +#include +#include +#include "gm12u320_drv.h" + +static int gm12u320_driver_set_busid(struct drm_device *d, struct drm_master *m) +{ + return 0; +} + +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, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + +static struct drm_driver driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .load = gm12u320_driver_load, + .unload = gm12u320_driver_unload, + .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 (!dev) + return -ENOMEM; + + 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); + drm_connector_unregister_all(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 "); +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 0000000..84df491 --- /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 + * Copyright (C) 2009 Roberto De Ioris + * Copyright (C) 2009 Jaya Kumar + * Copyright (C) 2009 Bernie Thompson + * + * 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 +#include +#include + +#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); +int 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_area_struct *vma, 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 0000000..085f05b --- /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 + * Copyright (C) 2009 Roberto De Ioris + * Copyright (C) 2009 Jaya Kumar + * Copyright (C) 2009 Bernie Thompson + * + * 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 +#include +#include +#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 0000000..d62de5b --- /dev/null +++ b/drivers/gpu/drm/gm12u320/gm12u320_fb.c @@ -0,0 +1,345 @@ +/* + * 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 + * Copyright (C) 2009 Roberto De Ioris + * Copyright (C) 2009 Jaya Kumar + * Copyright (C) 2009 Bernie Thompson + * + * 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 +#include +#include + +#include +#include +#include +#include "gm12u320_drv.h" + +#include + +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, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = drm_fb_helper_sys_fillrect, + .fb_copyarea = drm_fb_helper_sys_copyarea, + .fb_imageblit = drm_fb_helper_sys_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, + .fb_open = gm12u320_fb_open, +}; + +static struct fb_deferred_io gm12u320_fb_defio = { + .delay = HZ / 30, + .deferred_io = drm_fb_helper_deferred_io, +}; + +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(&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_destroy_fbi; + + 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; + info->fbdefio = &gm12u320_fb_defio; + drm_fb_helper_fill_fix(info, drm_fb->pitches[0], drm_fb->depth); + drm_fb_helper_fill_var(info, &fbdev->helper, + sizes->fb_width, sizes->fb_height); + fb_deferred_io_init(info); + + DRM_DEBUG_KMS("allocated %dx%d vmal %p\n", + drm_fb->width, drm_fb->height, + fbdev->fb.obj->vmapping); + + return ret; + +out_destroy_fbi: + drm_fb_helper_release_fbi(helper); +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) +{ + drm_fb_helper_unregister_fbi(&fbdev->helper); + drm_fb_helper_release_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, 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 0000000..d6784bf --- /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 +#include "gm12u320_drv.h" +#include +#include + +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_area_struct *vma, struct vm_fault *vmf) +{ + struct gm12u320_gem_object *obj = to_gm12u320_bo(vma->vm_private_data); + struct page *page; + unsigned int page_offset; + int ret = 0; + + page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> + PAGE_SHIFT; + + if (!obj->pages) + return VM_FAULT_SIGBUS; + + page = obj->pages[page_offset]; + ret = vm_insert_page(vma, (unsigned long)vmf->virtual_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; + } +} + +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 0000000..c28d138 --- /dev/null +++ b/drivers/gpu/drm/gm12u320/gm12u320_main.c @@ -0,0 +1,507 @@ +/* + * 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 + * Copyright (C) 2009 Roberto De Ioris + * Copyright (C) 2009 Jaya Kumar + * Copyright (C) 2009 Bernie Thompson + * + * 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 +#include +#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 msecs_to_jiffies(200) +#define DATA_TIMEOUT msecs_to_jiffies(1000) +#define IDLE_TIMEOUT msecs_to_jiffies(2000) +#define FIRST_FRAME_TIMEOUT msecs_to_jiffies(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), + 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; +} + +int 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); + return 0; +} + +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 0000000..4b6c767 --- /dev/null +++ b/drivers/gpu/drm/gm12u320/gm12u320_modeset.c @@ -0,0 +1,144 @@ +/* + * 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 + * Copyright (C) 2009 Roberto De Ioris + * Copyright (C) 2009 Jaya Kumar + * Copyright (C) 2009 Bernie Thompson + * + * 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 +#include +#include +#include +#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 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_send_vblank_event(dev, 0, 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, +}; + +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; + + drm_mode_create_dirty_info_property(dev); + + 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); +}