From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754517AbaIXIVE (ORCPT ); Wed, 24 Sep 2014 04:21:04 -0400 Received: from mail-wi0-f182.google.com ([209.85.212.182]:45232 "EHLO mail-wi0-f182.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754456AbaIXIUr (ORCPT ); Wed, 24 Sep 2014 04:20:47 -0400 Date: Wed, 24 Sep 2014 10:20:37 +0200 From: Daniel Vetter To: Mark yao Cc: heiko@sntech.de, Boris BREZILLON , David Airlie , Rob Clark , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Randy Dunlap , Grant Likely , Greg Kroah-Hartman , John Stultz , Rom Lemarchand , linux-doc@vger.kernel.org, kever.yang@rock-chips.com, dri-devel@lists.freedesktop.org, dianders@chromium.org, xjq@rock-chips.com, zyw@rock-chips.com, cym@rock-chips.com, linux-rockchip@lists.infradead.org, kfx@rock-chips.com, wxt@rock-chips.com, huangtao@rock-chips.com, devicetree@vger.kernel.org, yxj@rock-chips.com, marcheu@chromium.org, xxm@rock-chips.com, xw@rock-chips.com, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, cf@rock-chips.com Subject: Re: [PATCH v4 1/5] drm/rockchip: Add basic drm driver Message-ID: <20140924082037.GJ15734@phenom.ffwll.local> Mail-Followup-To: Mark yao , heiko@sntech.de, Boris BREZILLON , David Airlie , Rob Clark , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Randy Dunlap , Grant Likely , Greg Kroah-Hartman , John Stultz , Rom Lemarchand , linux-doc@vger.kernel.org, kever.yang@rock-chips.com, dri-devel@lists.freedesktop.org, dianders@chromium.org, xjq@rock-chips.com, zyw@rock-chips.com, cym@rock-chips.com, linux-rockchip@lists.infradead.org, kfx@rock-chips.com, wxt@rock-chips.com, huangtao@rock-chips.com, devicetree@vger.kernel.org, yxj@rock-chips.com, marcheu@chromium.org, xxm@rock-chips.com, xw@rock-chips.com, linux-api@vger.kernel.org, linux-kernel@vger.kernel.org, cf@rock-chips.com References: <1411382820-1615-1-git-send-email-mark.yao@rock-chips.com> <1411382934-1763-1-git-send-email-mark.yao@rock-chips.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1411382934-1763-1-git-send-email-mark.yao@rock-chips.com> X-Operating-System: Linux phenom 3.15.0-rc3+ User-Agent: Mutt/1.5.23 (2014-03-12) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon, Sep 22, 2014 at 06:48:54PM +0800, Mark yao wrote: > This patch adds the basic structure of a DRM Driver for Rockchip Socs. > > Signed-off-by: Mark yao > --- > Changes in v2: > - use the component framework to defer main drm driver probe > until all VOP devices have been probed. > - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by > master device and each vop device can shared the drm dma mapping. > - use drm_crtc_init_with_planes and drm_universal_plane_init. > - remove unnecessary middle layers. > - add cursor set, move funcs to rockchip drm crtc. > - use vop reset at first init > - reference framebuffer when used and unreference when swap out vop > > Changes in v3: > - change "crtc->fb" to "crtc->primary-fb" > Adviced by Daniel Vetter > - init cursor plane with universal api, remove unnecessary cursor set,move > > Changes in v4: > Adviced by David Herrmann > - remove drm_platform_*() usage, use register drm device directly. > Adviced by Rob Clark > - remove special mmap ioctl, do userspace mmap with normal mmap() or mmap offset > > drivers/gpu/drm/Kconfig | 2 + > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/rockchip/Kconfig | 19 + > drivers/gpu/drm/rockchip/Makefile | 10 + > drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 524 ++++++++++ > drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 120 +++ > drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 201 ++++ > drivers/gpu/drm/rockchip/rockchip_drm_fb.h | 28 + > drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 231 +++++ > drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 20 + > drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 404 ++++++++ > drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 72 ++ > drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1372 +++++++++++++++++++++++++ > drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 187 ++++ > include/uapi/drm/rockchip_drm.h | 75 ++ uapi is still here ... Was this an oversight? -Daniel > 15 files changed, 3266 insertions(+) > create mode 100644 drivers/gpu/drm/rockchip/Kconfig > create mode 100644 drivers/gpu/drm/rockchip/Makefile > create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c > create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h > create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c > create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h > create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c > create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h > create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c > create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h > create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.c > create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.h > create mode 100644 include/uapi/drm/rockchip_drm.h > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index b066bb3..7c4c3c6 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -171,6 +171,8 @@ config DRM_SAVAGE > > source "drivers/gpu/drm/exynos/Kconfig" > > +source "drivers/gpu/drm/rockchip/Kconfig" > + > source "drivers/gpu/drm/vmwgfx/Kconfig" > > source "drivers/gpu/drm/gma500/Kconfig" > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile > index 4a55d59..d03387a 100644 > --- a/drivers/gpu/drm/Makefile > +++ b/drivers/gpu/drm/Makefile > @@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ > obj-$(CONFIG_DRM_VIA) +=via/ > obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ > obj-$(CONFIG_DRM_EXYNOS) +=exynos/ > +obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/ > obj-$(CONFIG_DRM_GMA500) += gma500/ > obj-$(CONFIG_DRM_UDL) += udl/ > obj-$(CONFIG_DRM_AST) += ast/ > diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig > new file mode 100644 > index 0000000..7146c80 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/Kconfig > @@ -0,0 +1,19 @@ > +config DRM_ROCKCHIP > + tristate "DRM Support for Rockchip" > + depends on DRM && ROCKCHIP_IOMMU > + select ARM_DMA_USE_IOMMU > + select IOMMU_API > + select DRM_KMS_HELPER > + select DRM_KMS_FB_HELPER > + select DRM_PANEL > + select FB_CFB_FILLRECT > + select FB_CFB_COPYAREA > + select FB_CFB_IMAGEBLIT > + select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE > + select VIDEOMODE_HELPERS > + help > + Choose this option if you have a Rockchip soc chipset. > + This driver provides kernel mode setting and buffer > + management to userspace. This driver does not provides > + 2D or 3D acceleration; acceleration is performed by other > + IP found on the SoC. > diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile > new file mode 100644 > index 0000000..6e6d468 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/Makefile > @@ -0,0 +1,10 @@ > +# > +# Makefile for the drm device driver. This driver provides support for the > +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. > + > +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip > + > +rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \ > + rockchip_drm_gem.o rockchip_drm_vop.o > + > +obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c > new file mode 100644 > index 0000000..94926cb > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c > @@ -0,0 +1,524 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * based on exynos_drm_drv.c > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + */ > + > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +#include "rockchip_drm_drv.h" > +#include "rockchip_drm_fb.h" > +#include "rockchip_drm_fbdev.h" > +#include "rockchip_drm_gem.h" > + > +#define DRIVER_NAME "rockchip" > +#define DRIVER_DESC "RockChip Soc DRM" > +#define DRIVER_DATE "20140818" > +#define DRIVER_MAJOR 1 > +#define DRIVER_MINOR 0 > + > +/* > + * Attach a (component) device to the shared drm dma mapping from master drm > + * device. This is used by the VOPs to map GEM buffers to a common DMA > + * mapping. > + */ > +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, > + struct device *dev) > +{ > + struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping; > + int ret; > + > + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); > + if (ret) > + return ret; > + > + dma_set_max_seg_size(dev, 0xffffffffu); > + > + return arm_iommu_attach_device(dev, mapping); > +} > + > +void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, > + struct device *dev) > +{ > + arm_iommu_detach_device(drm_dev->dev); > +} > + > +static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) > +{ > + struct rockchip_drm_private *private; > + struct dma_iommu_mapping *mapping; > + struct device *dev = drm_dev->dev; > + int ret; > + > + private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); > + if (!private) > + return -ENOMEM; > + > + dev_set_drvdata(drm_dev->dev, dev); > + drm_dev->dev_private = private; > + > + drm_mode_config_init(drm_dev); > + > + rockchip_drm_mode_config_init(drm_dev); > + > + dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), > + GFP_KERNEL); > + if (!dev->dma_parms) { > + ret = -ENOMEM; > + goto err_config_cleanup; > + } > + > + /* TODO(djkurtz): fetch the mapping start/size from somewhere */ > + mapping = arm_iommu_create_mapping(&platform_bus_type, 0x10000000, > + SZ_1G); > + if (IS_ERR(mapping)) { > + ret = PTR_ERR(mapping); > + goto err_config_cleanup; > + } > + > + dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); > + dma_set_max_seg_size(dev, 0xffffffffu); > + > + ret = arm_iommu_attach_device(dev, mapping); > + if (ret) > + goto err_release_mapping; > + > + /* Try to bind all sub drivers. */ > + ret = component_bind_all(dev, drm_dev); > + if (ret) > + goto err_detach_device; > + > + /* init kms poll for handling hpd */ > + drm_kms_helper_poll_init(drm_dev); > + > + /* > + * enable drm irq mode. > + * - with irq_enabled = true, we can use the vblank feature. > + */ > + drm_dev->irq_enabled = true; > + > + /* > + * with vblank_disable_allowed = true, vblank interrupt will be disabled > + * by drm timer once a current process gives up ownership of > + * vblank event.(after drm_vblank_put function is called) > + */ > + drm_dev->vblank_disable_allowed = true; > + > + ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC); > + if (ret) > + goto err_kms_helper_poll_fini; > + > + rockchip_drm_fbdev_init(drm_dev); > + > + /* force connectors detection */ > + drm_helper_hpd_irq_event(drm_dev); > + > + return 0; > + > +err_kms_helper_poll_fini: > + drm_kms_helper_poll_fini(drm_dev); > + component_unbind_all(dev, drm_dev); > +err_detach_device: > + arm_iommu_detach_device(dev); > +err_release_mapping: > + arm_iommu_release_mapping(dev->archdata.mapping); > +err_config_cleanup: > + drm_mode_config_cleanup(drm_dev); > + drm_dev->dev_private = NULL; > + dev_set_drvdata(dev, NULL); > + return ret; > +} > + > +static int rockchip_drm_unload(struct drm_device *drm_dev) > +{ > + struct device *dev = drm_dev->dev; > + > + drm_kms_helper_poll_fini(drm_dev); > + component_unbind_all(dev, drm_dev); > + arm_iommu_detach_device(dev); > + arm_iommu_release_mapping(dev->archdata.mapping); > + drm_mode_config_cleanup(drm_dev); > + drm_dev->dev_private = NULL; > + dev_set_drvdata(dev, NULL); > + > + return 0; > +} > + > +static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state) > +{ > + struct drm_connector *connector; > + > + drm_modeset_lock_all(dev); > + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { > + int old_dpms = connector->dpms; > + > + if (connector->funcs->dpms) > + connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF); > + > + /* Set the old mode back to the connector for resume */ > + connector->dpms = old_dpms; > + } > + drm_modeset_unlock_all(dev); > + > + return 0; > +} > + > +static int rockchip_drm_resume(struct drm_device *dev) > +{ > + struct drm_connector *connector; > + > + drm_modeset_lock_all(dev); > + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { > + if (connector->funcs->dpms) > + connector->funcs->dpms(connector, connector->dpms); > + } > + drm_modeset_unlock_all(dev); > + > + drm_helper_resume_force_mode(dev); > + > + return 0; > +} > + > +void rockchip_drm_lastclose(struct drm_device *dev) > +{ > + struct rockchip_drm_private *priv = dev->dev_private; > + > + drm_modeset_lock_all(dev); > + if (priv->fb_helper) > + drm_fb_helper_restore_fbdev_mode(priv->fb_helper); > + drm_modeset_unlock_all(dev); > +} > + > +static const struct drm_ioctl_desc rockchip_ioctls[] = { > + DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl, > + DRM_UNLOCKED | DRM_AUTH), > + DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET, rockchip_gem_get_ioctl, > + DRM_UNLOCKED), > + DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET, > + rockchip_gem_map_offset_ioctl, DRM_UNLOCKED | > + DRM_AUTH), > +}; > + > +static const struct file_operations rockchip_drm_driver_fops = { > + .owner = THIS_MODULE, > + .open = drm_open, > + .mmap = rockchip_drm_gem_mmap, > + .poll = drm_poll, > + .read = drm_read, > + .unlocked_ioctl = drm_ioctl, > +#ifdef CONFIG_COMPAT > + .compat_ioctl = drm_compat_ioctl, > +#endif > + .release = drm_release, > +}; > + > +const struct vm_operations_struct rockchip_drm_vm_ops = { > + .open = drm_gem_vm_open, > + .close = drm_gem_vm_close, > +}; > + > +static struct drm_driver rockchip_drm_driver = { > + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, > + .load = rockchip_drm_load, > + .unload = rockchip_drm_unload, > + .lastclose = rockchip_drm_lastclose, > + .suspend = rockchip_drm_suspend, > + .resume = rockchip_drm_resume, > + .get_vblank_counter = drm_vblank_count, > + .enable_vblank = rockchip_drm_crtc_enable_vblank, > + .disable_vblank = rockchip_drm_crtc_disable_vblank, > + .gem_vm_ops = &rockchip_drm_vm_ops, > + .gem_free_object = rockchip_gem_free_object, > + .dumb_create = rockchip_gem_dumb_create, > + .dumb_map_offset = rockchip_gem_dumb_map_offset, > + .dumb_destroy = drm_gem_dumb_destroy, > + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, > + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, > + .gem_prime_import = drm_gem_prime_import, > + .gem_prime_export = drm_gem_prime_export, > + .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table, > + .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table, > + .gem_prime_vmap = rockchip_gem_prime_vmap, > + .gem_prime_vunmap = rockchip_gem_prime_vunmap, > + .gem_prime_mmap = rockchip_gem_prime_mmap, > + .ioctls = rockchip_ioctls, > + .num_ioctls = ARRAY_SIZE(rockchip_ioctls), > + .fops = &rockchip_drm_driver_fops, > + .name = DRIVER_NAME, > + .desc = DRIVER_DESC, > + .date = DRIVER_DATE, > + .major = DRIVER_MAJOR, > + .minor = DRIVER_MINOR, > +}; > + > +#ifdef CONFIG_PM_SLEEP > +static int rockchip_drm_sys_suspend(struct device *dev) > +{ > + struct drm_device *drm_dev = dev_get_drvdata(dev); > + pm_message_t message; > + > + if (pm_runtime_suspended(dev)) > + return 0; > + > + message.event = PM_EVENT_SUSPEND; > + > + return rockchip_drm_suspend(drm_dev, message); > +} > + > +static int rockchip_drm_sys_resume(struct device *dev) > +{ > + struct drm_device *drm_dev = dev_get_drvdata(dev); > + > + if (pm_runtime_suspended(dev)) > + return 0; > + > + return rockchip_drm_resume(drm_dev); > +} > +#endif > + > +static const struct dev_pm_ops rockchip_drm_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend, > + rockchip_drm_sys_resume) > +}; > + > +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, > + struct device_node *np) > +{ > + struct rockchip_drm_private *priv = drm->dev_private; > + struct device_node *port; > + int pipe; > + > + if (priv->num_pipe >= ROCKCHIP_MAX_CRTC) > + return -EINVAL; > + > + port = of_get_child_by_name(np, "port"); > + of_node_put(np); > + if (!port) { > + dev_err(drm->dev, "no port node found in %s\n", > + np->full_name); > + return -ENXIO; > + } > + pipe = priv->num_pipe++; > + crtc->port = port; > + > + priv->crtc[pipe] = crtc; > + > + return pipe; > +} > + > +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe) > +{ > + struct rockchip_drm_private *priv = drm->dev_private; > + > + priv->num_pipe--; > + of_node_put(priv->crtc[pipe]->port); > + priv->crtc[pipe] = NULL; > +} > + > +struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe) > +{ > + struct rockchip_drm_private *priv = drm->dev_private; > + > + if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe]) > + return priv->crtc[pipe]; > + > + return NULL; > +} > + > +/* > + * @node: device tree node containing encoder input ports > + * @encoder: drm_encoder > + */ > +int rockchip_drm_encoder_get_mux_id(struct device_node *node, > + struct drm_encoder *encoder) > +{ > + struct device_node *ep = NULL; > + struct drm_crtc *crtc = encoder->crtc; > + struct of_endpoint endpoint; > + struct device_node *port; > + int ret; > + > + if (!node || !crtc) > + return -EINVAL; > + > + do { > + ep = of_graph_get_next_endpoint(node, ep); > + if (!ep) > + break; > + > + port = of_graph_get_remote_port(ep); > + of_node_put(port); > + if (port == crtc->port) { > + ret = of_graph_parse_endpoint(ep, &endpoint); > + return ret ? ret : endpoint.id; > + } > + } while (ep); > + > + return -EINVAL; > +} > + > +static int compare_of(struct device *dev, void *data) > +{ > + struct device_node *np = data; > + > + return dev->of_node == np; > +} > + > +static void rockchip_add_endpoints(struct device *dev, > + struct component_match **match, > + struct device_node *port) > +{ > + struct device_node *ep, *remote; > + > + for_each_child_of_node(port, ep) { > + remote = of_graph_get_remote_port_parent(ep); > + if (!remote || !of_device_is_available(remote)) { > + of_node_put(remote); > + continue; > + } else if (!of_device_is_available(remote->parent)) { > + dev_warn(dev, "parent device of %s is not available\n", > + remote->full_name); > + of_node_put(remote); > + continue; > + } > + > + component_match_add(dev, match, compare_of, remote); > + of_node_put(remote); > + } > +} > + > +static int rockchip_drm_bind(struct device *dev) > +{ > + struct drm_device *drm; > + int ret; > + > + drm = drm_dev_alloc(&rockchip_drm_driver, dev); > + if (!drm) > + return -ENOMEM; > + > + ret = drm_dev_set_unique(drm, "%s", dev_name(dev)); > + if (ret) > + goto err_free; > + > + ret = drm_dev_register(drm, 0); > + if (ret) > + goto err_free; > + > + dev_set_drvdata(dev, drm); > + > + return 0; > + > +err_free: > + drm_dev_unref(drm); > + return ret; > +} > + > +static void rockchip_drm_unbind(struct device *dev) > +{ > + struct drm_device *drm = dev_get_drvdata(dev); > + > + drm_dev_unregister(drm); > + drm_dev_unref(drm); > +} > + > +static const struct component_master_ops rockchip_drm_ops = { > + .bind = rockchip_drm_bind, > + .unbind = rockchip_drm_unbind, > +}; > + > +static int rockchip_drm_platform_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct component_match *match = NULL; > + struct device_node *np = dev->of_node; > + struct device_node *port; > + int i; > + int ret; > + > + if (!np) > + return -ENODEV; > + /* > + * Bind the crtc ports first, so that > + * drm_of_find_possible_crtcs called from encoder .bind callbacks > + * works as expected. > + */ > + for (i = 0;; i++) { > + port = of_parse_phandle(np, "ports", i); > + if (!port) > + break; > + > + component_match_add(dev, &match, compare_of, port->parent); > + of_node_put(port); > + } > + > + if (i == 0) { > + dev_err(dev, "missing 'ports' property\n"); > + return -ENODEV; > + } > + /* > + * For each bound crtc, bind the encoders attached to its > + * remote endpoint. > + */ > + for (i = 0;; i++) { > + port = of_parse_phandle(np, "ports", i); > + if (!port) > + break; > + > + rockchip_add_endpoints(dev, &match, port); > + of_node_put(port); > + } > + > + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); > + if (ret) > + return ret; > + > + return component_master_add_with_match(dev, &rockchip_drm_ops, match); > +} > + > +static int rockchip_drm_platform_remove(struct platform_device *pdev) > +{ > + component_master_del(&pdev->dev, &rockchip_drm_ops); > + return 0; > +} > + > +static const struct of_device_id rockchip_drm_dt_ids[] = { > + { .compatible = "rockchip,display-subsystem", }, > + { /* sentinel */ }, > +}; > +MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids); > + > +static struct platform_driver rockchip_drm_platform_driver = { > + .probe = rockchip_drm_platform_probe, > + .remove = rockchip_drm_platform_remove, > + .driver = { > + .owner = THIS_MODULE, > + .name = "rockchip-drm", > + .of_match_table = rockchip_drm_dt_ids, > + }, > +}; > + > +module_platform_driver(rockchip_drm_platform_driver); > + > +MODULE_AUTHOR("Mark Yao "); > +MODULE_DESCRIPTION("ROCKCHIP DRM Driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h > new file mode 100644 > index 0000000..154b3ec > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h > @@ -0,0 +1,120 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * based on exynos_drm_drv.h > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + */ > + > +#ifndef _ROCKCHIP_DRM_DRV_H > +#define _ROCKCHIP_DRM_DRV_H > + > +#include > +#include > + > +#define ROCKCHIP_MAX_FB_BUFFER 4 > +#define ROCKCHIP_MAX_CONNECTOR 2 > + > +struct drm_device; > +struct drm_connector; > + > +/* > + * display output interface supported by rockchip lcdc > + */ > +#define ROCKCHIP_OUTFACE_P888 0 > +#define ROCKCHIP_OUTFACE_P666 1 > +#define ROCKCHIP_OUTFACE_P565 2 > +/* for use special outface */ > +#define ROCKCHIP_OUTFACE_AAAA 15 > + > +#define ROCKCHIP_COLOR_SWAP_RG 0x1 > +#define ROCKCHIP_COLOR_SWAP_RB 0x2 > +#define ROCKCHIP_COLOR_SWAP_GB 0x4 > + > +/* > + * Special mode info for rockchip > + * > + * @out_type: lcd controller need to know the sceen type. > + */ > +struct rockchip_display_mode { > + int out_type; > +}; > + > +#define ROCKCHIP_EVENT_HOTPLUG 1 > + > +enum rockchip_plane_type { > + ROCKCHIP_WIN0, > + ROCKCHIP_WIN1, > + ROCKCHIP_WIN2, > + ROCKCHIP_WIN3, > + ROCKCHIP_CURSOR, > + ROCKCHIP_MAX_PLANE, > +}; > + > +/* This enumerates device type. */ > +enum rockchip_drm_device_type { > + ROCKCHIP_DEVICE_TYPE_NONE, > + ROCKCHIP_DEVICE_TYPE_CRTC, > + ROCKCHIP_DEVICE_TYPE_CONNECTOR, > +}; > + > +/* this enumerates display type. */ > +enum rockchip_drm_output_type { > + ROCKCHIP_DISPLAY_TYPE_NONE = 0, > + /* RGB Interface. */ > + ROCKCHIP_DISPLAY_TYPE_RGB, > + /* LVDS Interface. */ > + ROCKCHIP_DISPLAY_TYPE_LVDS, > + /* EDP Interface. */ > + ROCKCHIP_DISPLAY_TYPE_EDP, > + /* MIPI Interface. */ > + ROCKCHIP_DISPLAY_TYPE_MIPI, > + /* HDMI Interface. */ > + ROCKCHIP_DISPLAY_TYPE_HDMI, > +}; > + > +enum rockchip_crtc_type { > + ROCKCHIP_CRTC_VOPB, > + ROCKCHIP_CRTC_VOPL, > + ROCKCHIP_MAX_CRTC, > +}; > + > +/* > + * Rockchip drm private structure. > + * > + * @num_pipe: number of pipes for this device. > + */ > +struct rockchip_drm_private { > + struct drm_fb_helper *fb_helper; > + /* > + * created crtc object would be contained at this array and > + * this array is used to be aware of which crtc did it request vblank. > + */ > + struct drm_crtc *crtc[ROCKCHIP_MAX_CRTC]; > + > + unsigned int num_pipe; > +}; > + > +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc, > + struct device_node *port); > +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe); > +struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe); > +int rockchip_drm_encoder_get_mux_id(struct device_node *node, > + struct drm_encoder *encoder); > +void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe); > +void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev); > +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe); > +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe); > +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, > + struct device *dev); > +void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, > + struct device *dev); > +#endif /* _ROCKCHIP_DRM_DRV_H_ */ > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c > new file mode 100644 > index 0000000..b319505 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c > @@ -0,0 +1,201 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + */ > + > +#include > +#include > +#include > +#include > +#include > + > +#include "rockchip_drm_drv.h" > +#include "rockchip_drm_gem.h" > + > +#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb) > + > +struct rockchip_drm_fb { > + struct drm_framebuffer fb; > + struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER]; > +}; > + > +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb, > + unsigned int plane) > +{ > + struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb); > + > + if (plane >= ROCKCHIP_MAX_FB_BUFFER) > + return NULL; > + > + return rk_fb->obj[plane]; > +} > + > +static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb) > +{ > + struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb); > + struct drm_gem_object *obj; > + int i; > + > + for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) { > + obj = rockchip_fb->obj[i]; > + if (obj) > + drm_gem_object_unreference_unlocked(obj); > + } > + > + drm_framebuffer_cleanup(fb); > + kfree(rockchip_fb); > +} > + > +static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb, > + struct drm_file *file_priv, > + unsigned int *handle) > +{ > + struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb); > + > + return drm_gem_handle_create(file_priv, > + rockchip_fb->obj[0], handle); > +} > + > +static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = { > + .destroy = rockchip_drm_fb_destroy, > + .create_handle = rockchip_drm_fb_create_handle, > +}; > + > +static struct rockchip_drm_fb * > +rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, > + struct drm_gem_object **obj, unsigned int num_planes) > +{ > + struct rockchip_drm_fb *rockchip_fb; > + int ret; > + int i; > + > + rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL); > + if (!rockchip_fb) > + return ERR_PTR(-ENOMEM); > + > + drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd); > + > + for (i = 0; i < num_planes; i++) > + rockchip_fb->obj[i] = obj[i]; > + > + ret = drm_framebuffer_init(dev, &rockchip_fb->fb, > + &rockchip_drm_fb_funcs); > + if (ret) { > + dev_err(dev->dev, "Failed to initialize framebuffer: %d\n", > + ret); > + kfree(rockchip_fb); > + return ERR_PTR(ret); > + } > + > + return rockchip_fb; > +} > + > +static struct drm_framebuffer * > +rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, > + struct drm_mode_fb_cmd2 *mode_cmd) > +{ > + struct rockchip_drm_fb *rockchip_fb; > + struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER]; > + struct drm_gem_object *obj; > + unsigned int hsub; > + unsigned int vsub; > + int num_planes; > + int ret; > + int i; > + > + hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); > + vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); > + num_planes = min(drm_format_num_planes(mode_cmd->pixel_format), > + ROCKCHIP_MAX_FB_BUFFER); > + > + for (i = 0; i < num_planes; i++) { > + unsigned int width = mode_cmd->width / (i ? hsub : 1); > + unsigned int height = mode_cmd->height / (i ? vsub : 1); > + unsigned int min_size; > + > + obj = drm_gem_object_lookup(dev, file_priv, > + mode_cmd->handles[i]); > + if (!obj) { > + dev_err(dev->dev, "Failed to lookup GEM object\n"); > + ret = -ENXIO; > + goto err_gem_object_unreference; > + } > + > + min_size = (height - 1) * mode_cmd->pitches[i] + > + mode_cmd->offsets[i] + > + width * drm_format_plane_cpp(mode_cmd->pixel_format, i); > + > + if (obj->size < min_size) { > + drm_gem_object_unreference_unlocked(obj); > + ret = -EINVAL; > + goto err_gem_object_unreference; > + } > + objs[i] = obj; > + } > + > + rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i); > + if (IS_ERR(rockchip_fb)) { > + ret = PTR_ERR(rockchip_fb); > + goto err_gem_object_unreference; > + } > + > + return &rockchip_fb->fb; > + > +err_gem_object_unreference: > + for (i--; i >= 0; i--) > + drm_gem_object_unreference_unlocked(objs[i]); > + return ERR_PTR(ret); > +} > + > +static void rockchip_drm_output_poll_changed(struct drm_device *dev) > +{ > + struct rockchip_drm_private *private = dev->dev_private; > + struct drm_fb_helper *fb_helper = private->fb_helper; > + > + if (fb_helper) > + drm_fb_helper_hotplug_event(fb_helper); > +} > + > +static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { > + .fb_create = rockchip_user_fb_create, > + .output_poll_changed = rockchip_drm_output_poll_changed, > +}; > + > +struct drm_framebuffer * > +rockchip_drm_framebuffer_init(struct drm_device *dev, > + struct drm_mode_fb_cmd2 *mode_cmd, > + struct drm_gem_object *obj) > +{ > + struct rockchip_drm_fb *rockchip_fb; > + > + rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1); > + if (IS_ERR(rockchip_fb)) > + return NULL; > + > + return &rockchip_fb->fb; > +} > + > +void rockchip_drm_mode_config_init(struct drm_device *dev) > +{ > + dev->mode_config.min_width = 0; > + dev->mode_config.min_height = 0; > + > + /* > + * set max width and height as default value(4096x4096). > + * this value would be used to check framebuffer size limitation > + * at drm_mode_addfb(). > + */ > + dev->mode_config.max_width = 4096; > + dev->mode_config.max_height = 4096; > + > + dev->mode_config.funcs = &rockchip_drm_mode_config_funcs; > +} > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h > new file mode 100644 > index 0000000..09574d4 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h > @@ -0,0 +1,28 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + */ > + > +#ifndef _ROCKCHIP_DRM_FB_H > +#define _ROCKCHIP_DRM_FB_H > + > +struct drm_framebuffer * > +rockchip_drm_framebuffer_init(struct drm_device *dev, > + struct drm_mode_fb_cmd2 *mode_cmd, > + struct drm_gem_object *obj); > +void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb); > + > +void rockchip_drm_mode_config_init(struct drm_device *dev); > + > +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb, > + unsigned int plane); > +#endif /* _ROCKCHIP_DRM_FB_H */ > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c > new file mode 100644 > index 0000000..fe1bb22 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c > @@ -0,0 +1,231 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + */ > + > +#include > +#include > +#include > + > +#include > + > +#include "rockchip_drm_drv.h" > +#include "rockchip_drm_gem.h" > +#include "rockchip_drm_fb.h" > + > +#define PREFERRED_BPP 32 > +#define to_rockchip_fbdev(x) container_of(x, struct rockchip_fbdev, helper) > + > +struct rockchip_fbdev { > + struct drm_fb_helper helper; > + struct drm_gem_object *bo; > +}; > + > +static int rockchip_fbdev_mmap(struct fb_info *info, > + struct vm_area_struct *vma) > +{ > + struct drm_fb_helper *helper = info->par; > + struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper); > + > + return rockchip_gem_mmap(fbdev->bo, vma); > +} > + > +static struct fb_ops rockchip_drm_fbdev_ops = { > + .owner = THIS_MODULE, > + .fb_mmap = rockchip_fbdev_mmap, > + .fb_fillrect = cfb_fillrect, > + .fb_copyarea = cfb_copyarea, > + .fb_imageblit = cfb_imageblit, > + .fb_check_var = drm_fb_helper_check_var, > + .fb_set_par = drm_fb_helper_set_par, > + .fb_blank = drm_fb_helper_blank, > + .fb_pan_display = drm_fb_helper_pan_display, > + .fb_setcmap = drm_fb_helper_setcmap, > +}; > + > +static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, > + struct drm_fb_helper_surface_size *sizes) > +{ > + struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper); > + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; > + struct drm_device *dev = helper->dev; > + struct rockchip_gem_object *rk_obj; > + struct drm_framebuffer *fb; > + unsigned int bytes_per_pixel; > + unsigned long offset; > + struct fb_info *fbi; > + size_t size; > + int ret; > + > + bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); > + > + mode_cmd.width = sizes->surface_width; > + mode_cmd.height = sizes->surface_height; > + mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; > + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, > + sizes->surface_depth); > + > + size = mode_cmd.pitches[0] * mode_cmd.height; > + > + rk_obj = rockchip_gem_create_object(dev, size); > + if (IS_ERR(rk_obj)) > + return -ENOMEM; > + > + fbdev->bo = &rk_obj->base; > + > + fbi = framebuffer_alloc(0, dev->dev); > + if (!fbi) { > + dev_err(dev->dev, "Failed to allocate framebuffer info.\n"); > + ret = -ENOMEM; > + goto err_rockchip_gem_free_object; > + } > + > + helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, fbdev->bo); > + if (IS_ERR(helper->fb)) { > + dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); > + ret = PTR_ERR(helper->fb); > + goto err_framebuffer_release; > + } > + > + helper->fbdev = fbi; > + > + fbi->par = helper; > + fbi->flags = FBINFO_FLAG_DEFAULT; > + fbi->fbops = &rockchip_drm_fbdev_ops; > + > + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); > + if (ret) { > + dev_err(dev->dev, "Failed to allocate color map.\n"); > + goto err_drm_framebuffer_unref; > + } > + > + fb = helper->fb; > + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); > + drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); > + > + offset = fbi->var.xoffset * bytes_per_pixel; > + offset += fbi->var.yoffset * fb->pitches[0]; > + > + dev->mode_config.fb_base = 0; > + fbi->screen_base = rk_obj->kvaddr + offset; > + fbi->screen_size = rk_obj->base.size; > + fbi->fix.smem_len = rk_obj->base.size; > + > + DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n", > + fb->width, fb->height, fb->depth, rk_obj->kvaddr, > + offset, size); > + return 0; > + > +err_drm_framebuffer_unref: > + drm_framebuffer_unreference(helper->fb); > +err_framebuffer_release: > + framebuffer_release(fbi); > +err_rockchip_gem_free_object: > + rockchip_gem_free_object(&rk_obj->base); > + return ret; > +} > + > +static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = { > + .fb_probe = rockchip_drm_fbdev_create, > +}; > + > +int rockchip_drm_fbdev_init(struct drm_device *dev) > +{ > + struct rockchip_drm_private *private = dev->dev_private; > + struct rockchip_fbdev *fbdev; > + struct drm_fb_helper *helper; > + unsigned int num_crtc; > + int ret; > + > + if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) > + return -EINVAL; > + > + if (private->fb_helper) { > + DRM_ERROR("no allow to reinit fbdev\n"); > + return -EINVAL; > + } > + > + num_crtc = dev->mode_config.num_crtc; > + > + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); > + if (!fbdev) > + return -ENOMEM; > + > + fbdev->helper.funcs = &rockchip_drm_fb_helper_funcs; > + helper = &fbdev->helper; > + > + ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR); > + if (ret < 0) { > + dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); > + goto err_free; > + } > + > + ret = drm_fb_helper_single_add_all_connectors(helper); > + if (ret < 0) { > + dev_err(dev->dev, "Failed to add connectors.\n"); > + goto err_drm_fb_helper_fini; > + } > + > + /* disable all the possible outputs/crtcs before entering KMS mode */ > + drm_helper_disable_unused_functions(dev); > + > + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); > + if (ret < 0) { > + dev_err(dev->dev, "Failed to set initial hw configuration.\n"); > + goto err_drm_fb_helper_fini; > + } > + > + private->fb_helper = helper; > + > + return 0; > + > +err_drm_fb_helper_fini: > + drm_fb_helper_fini(helper); > +err_free: > + kfree(fbdev); > + return ret; > +} > + > +void rockchip_drm_fbdev_fini(struct drm_device *dev) > +{ > + struct rockchip_drm_private *private = dev->dev_private; > + struct drm_fb_helper *helper; > + struct rockchip_fbdev *fbdev; > + > + if (!private || !private->fb_helper) > + return; > + > + helper = private->fb_helper; > + fbdev = to_rockchip_fbdev(helper); > + > + if (helper->fbdev) { > + struct fb_info *info; > + int ret; > + > + info = helper->fbdev; > + ret = unregister_framebuffer(info); > + if (ret < 0) > + DRM_DEBUG_KMS("failed unregister_framebuffer()\n"); > + > + if (info->cmap.len) > + fb_dealloc_cmap(&info->cmap); > + > + framebuffer_release(info); > + } > + > + if (helper->fb) > + drm_framebuffer_unreference(helper->fb); > + > + drm_fb_helper_fini(helper); > + kfree(fbdev); > + private->fb_helper = NULL; > +} > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h > new file mode 100644 > index 0000000..5edcf6a > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h > @@ -0,0 +1,20 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + */ > + > +#ifndef _ROCKCHIP_DRM_FBDEV_H > +#define _ROCKCHIP_DRM_FBDEV_H > + > +int rockchip_drm_fbdev_init(struct drm_device *dev); > + > +#endif /* _ROCKCHIP_DRM_FBDEV_H */ > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c > new file mode 100644 > index 0000000..2f34e92 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c > @@ -0,0 +1,404 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + */ > + > +#include > +#include > +#include > + > +#include > +#include > + > +#include "rockchip_drm_drv.h" > +#include "rockchip_drm_gem.h" > + > +static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj) > +{ > + struct drm_gem_object *obj = &rk_obj->base; > + struct drm_device *drm = obj->dev; > + > + init_dma_attrs(&rk_obj->dma_attrs); > + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs); > + > + /* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */ > + rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size, > + &rk_obj->dma_addr, GFP_KERNEL, > + &rk_obj->dma_attrs); > + if (IS_ERR(rk_obj->kvaddr)) { > + int ret = PTR_ERR(rk_obj->kvaddr); > + > + DRM_ERROR("failed to allocate %#x byte dma buffer, %d", > + obj->size, ret); > + return ret; > + } > + > + return 0; > +} > + > +static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj) > +{ > + struct drm_gem_object *obj = &rk_obj->base; > + struct drm_device *drm = obj->dev; > + > + dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr, > + &rk_obj->dma_attrs); > +} > + > +/* drm driver mmap file operations */ > +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) > +{ > + struct drm_file *priv = filp->private_data; > + struct drm_device *dev = priv->minor->dev; > + struct drm_gem_object *obj; > + struct drm_vma_offset_node *node; > + int ret; > + > + if (drm_device_is_unplugged(dev)) > + return -ENODEV; > + > + mutex_lock(&dev->struct_mutex); > + > + node = drm_vma_offset_exact_lookup(dev->vma_offset_manager, > + vma->vm_pgoff, > + vma_pages(vma)); > + if (!node) { > + mutex_unlock(&dev->struct_mutex); > + DRM_ERROR("failed to find vma node.\n"); > + return -EINVAL; > + } else if (!drm_vma_node_is_allowed(node, filp)) { > + mutex_unlock(&dev->struct_mutex); > + return -EACCES; > + } > + > + obj = container_of(node, struct drm_gem_object, vma_node); > + ret = rockchip_gem_mmap(obj, vma); > + > + mutex_unlock(&dev->struct_mutex); > + > + return ret; > +} > + > +int rockchip_drm_gem_mmap_buffer(struct file *filp, > + struct vm_area_struct *vma) > +{ > + struct drm_gem_object *obj = filp->private_data; > + > + return rockchip_gem_mmap(obj, vma); > +} > + > +static const struct file_operations rockchip_drm_gem_fops = { > + .mmap = rockchip_drm_gem_mmap_buffer, > +}; > + > +struct rockchip_gem_object * > + rockchip_gem_create_object(struct drm_device *drm, unsigned int size) > +{ > + struct rockchip_gem_object *rk_obj; > + struct drm_gem_object *obj; > + struct file *filp; > + int ret; > + > + size = round_up(size, PAGE_SIZE); > + > + rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL); > + if (!rk_obj) > + return ERR_PTR(-ENOMEM); > + > + obj = &rk_obj->base; > + > + drm_gem_private_object_init(drm, obj, size); > + > + filp = anon_inode_getfile("rockchip_gem", &rockchip_drm_gem_fops, > + obj, 0); > + if (IS_ERR(filp)) { > + DRM_ERROR("failed to create anon file object.\n"); > + ret = PTR_ERR(filp); > + goto err_free_rk_obj; > + } > + filp->f_mode = FMODE_READ | FMODE_WRITE; > + obj->filp = filp; > + > + ret = drm_gem_create_mmap_offset(obj); > + if (ret) > + goto err_free_obj; > + > + ret = rockchip_gem_alloc_buf(rk_obj); > + if (ret) > + goto err_free_mmap_offset; > + > + return rk_obj; > + > +err_free_mmap_offset: > + drm_gem_free_mmap_offset(obj); > +err_free_obj: > + drm_gem_object_release(obj); > +err_free_rk_obj: > + kfree(rk_obj); > + return ERR_PTR(ret); > +} > + > +/* > + * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback > + * function > + */ > +void rockchip_gem_free_object(struct drm_gem_object *obj) > +{ > + struct rockchip_gem_object *rk_obj; > + > + drm_gem_free_mmap_offset(obj); > + > + rk_obj = to_rockchip_obj(obj); > + > + rockchip_gem_free_buf(rk_obj); > + drm_gem_free_mmap_offset(obj); > + > + drm_gem_object_release(obj); > + > + kfree(rk_obj); > +} > + > +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) > +{ > + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); > + struct drm_device *drm = obj->dev; > + unsigned long vm_size; > + > + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; > + vm_size = vma->vm_end - vma->vm_start; > + > + if (vm_size > obj->size) > + return -EINVAL; > + > + return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr, > + obj->size, &rk_obj->dma_attrs); > +} > + > +/* > + * rockchip_gem_create_with_handle - allocate an object with the given > + * size and create a gem handle on it > + * > + * returns a struct rockchip_gem_object* on success or ERR_PTR values > + * on failure. > + */ > +static struct rockchip_gem_object * > +rockchip_gem_create_with_handle(struct drm_file *file_priv, > + struct drm_device *drm, unsigned int size, > + unsigned int *handle) > +{ > + struct rockchip_gem_object *rk_obj; > + struct drm_gem_object *obj; > + int ret; > + > + rk_obj = rockchip_gem_create_object(drm, size); > + if (IS_ERR(rk_obj)) > + return NULL; > + > + obj = &rk_obj->base; > + > + /* > + * allocate a id of idr table where the obj is registered > + * and handle has the id what user can see. > + */ > + ret = drm_gem_handle_create(file_priv, obj, handle); > + if (ret) > + goto err_handle_create; > + > + /* drop reference from allocate - handle holds it now. */ > + drm_gem_object_unreference_unlocked(obj); > + > + return rk_obj; > + > +err_handle_create: > + rockchip_gem_free_object(obj); > + > + return ERR_PTR(ret); > +} > + > +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv, > + struct drm_device *dev, uint32_t handle, > + uint64_t *offset) > +{ > + struct drm_gem_object *obj; > + int ret = 0; > + > + mutex_lock(&dev->struct_mutex); > + > + /* > + * get offset of memory allocated for drm framebuffer. > + * - this callback would be called by user application > + * with DRM_IOCTL_MODE_MAP_DUMB command. > + */ > + > + obj = drm_gem_object_lookup(dev, file_priv, handle); > + if (!obj) { > + DRM_ERROR("failed to lookup gem object.\n"); > + ret = -EINVAL; > + goto unlock; > + } > + > + ret = drm_gem_create_mmap_offset(obj); > + if (ret) > + goto out; > + > + *offset = drm_vma_node_offset_addr(&obj->vma_node); > + DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); > + > +out: > + drm_gem_object_unreference(obj); > +unlock: > + mutex_unlock(&dev->struct_mutex); > + return ret; > +} > + > +/* > + * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback > + * function > + * > + * This aligns the pitch and size arguments to the minimum required. wrap > + * this into your own function if you need bigger alignment. > + */ > +int rockchip_gem_dumb_create(struct drm_file *file_priv, > + struct drm_device *dev, > + struct drm_mode_create_dumb *args) > +{ > + struct rockchip_gem_object *rk_obj; > + int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); > + > + if (args->pitch < min_pitch) > + args->pitch = min_pitch; > + > + if (args->size < args->pitch * args->height) > + args->size = args->pitch * args->height; > + > + rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size, > + &args->handle); > + > + return PTR_ERR_OR_ZERO(rk_obj); > +} > + > +int rockchip_gem_get_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_rockchip_gem_info *args = data; > + struct rockchip_gem_object *rk_obj; > + struct drm_gem_object *obj; > + > + mutex_lock(&dev->struct_mutex); > + > + obj = drm_gem_object_lookup(dev, file_priv, args->handle); > + if (!obj) { > + DRM_ERROR("failed to lookup gem object.\n"); > + mutex_unlock(&dev->struct_mutex); > + return -EINVAL; > + } > + > + rk_obj = to_rockchip_obj(obj); > + > + args->flags = rk_obj->flags; > + args->size = obj->size; > + > + drm_gem_object_unreference(obj); > + mutex_unlock(&dev->struct_mutex); > + > + return 0; > +} > + > +int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_rockchip_gem_map_off *args = data; > + > + return rockchip_gem_dumb_map_offset(file_priv, drm, args->handle, > + &args->offset); > +} > + > +int rockchip_gem_create_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file_priv) > +{ > + struct drm_rockchip_gem_create *args = data; > + struct rockchip_gem_object *rk_obj; > + > + rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size, > + &args->handle); > + return PTR_ERR_OR_ZERO(rk_obj); > +} > + > +/* > + * Allocate a sg_table for this GEM object. > + * Note: Both the table's contents, and the sg_table itself must be freed by > + * the caller. > + * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error. > + */ > +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj) > +{ > + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); > + struct drm_device *drm = obj->dev; > + struct sg_table *sgt = NULL; > + int ret; > + > + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); > + if (!sgt) > + return ERR_PTR(-ENOMEM); > + > + ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr, > + rk_obj->dma_addr, obj->size, > + &rk_obj->dma_attrs); > + if (ret) { > + DRM_ERROR("failed to allocate sgt, %d\n", ret); > + kfree(sgt); > + return ERR_PTR(ret); > + } > + > + return sgt; > +} > + > +struct drm_gem_object * > +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size, > + struct sg_table *sgt) > +{ > + struct rockchip_gem_object *rk_obj; > + > + if (sgt->nents != 1) > + return ERR_PTR(-EINVAL); > + > + rk_obj = rockchip_gem_create_object(dev, size); > + if (IS_ERR(rk_obj)) > + return ERR_PTR(-ENOMEM); > + > + return &rk_obj->base; > +} > + > +void *rockchip_gem_prime_vmap(struct drm_gem_object *obj) > +{ > + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); > + > + return rk_obj->kvaddr; > +} > + > +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) > +{ > + /* Nothing to do */ > +} > + > +int rockchip_gem_prime_mmap(struct drm_gem_object *obj, > + struct vm_area_struct *vma) > +{ > + struct drm_device *dev = obj->dev; > + int ret; > + > + mutex_lock(&dev->struct_mutex); > + ret = drm_gem_mmap_obj(obj, obj->size, vma); > + mutex_unlock(&dev->struct_mutex); > + > + return ret; > +} > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h > new file mode 100644 > index 0000000..6277dbd > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h > @@ -0,0 +1,72 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + */ > + > +#ifndef _ROCKCHIP_DRM_GEM_H > +#define _ROCKCHIP_DRM_GEM_H > + > +#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base) > + > +struct rockchip_gem_object { > + struct drm_gem_object base; > + unsigned int flags; > + > + void *kvaddr; > + dma_addr_t dma_addr; > + struct dma_attrs dma_attrs; > +}; > + > +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj); > +struct drm_gem_object * > +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size, > + struct sg_table *sgt); > +void *rockchip_gem_prime_vmap(struct drm_gem_object *obj); > +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); > +int rockchip_gem_prime_mmap(struct drm_gem_object *obj, > + struct vm_area_struct *vma); > + > +/* drm driver mmap file operations */ > +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); > + > +/* mmap a gem object to userspace. */ > +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); > + > +struct rockchip_gem_object * > + rockchip_gem_create_object(struct drm_device *drm, unsigned int size); > + > +void rockchip_gem_free_object(struct drm_gem_object *obj); > + > +int rockchip_gem_dumb_create(struct drm_file *file_priv, > + struct drm_device *dev, > + struct drm_mode_create_dumb *args); > +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv, > + struct drm_device *dev, uint32_t handle, > + uint64_t *offset); > +int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data, > + struct drm_file *file_priv); > +/* > + * request gem object creation and buffer allocation as the size > + * that it is calculated with framebuffer information such as width, > + * height and bpp. > + */ > +int rockchip_gem_create_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file_priv); > + > +/* get buffer offset to map to user space. */ > +int rockchip_gem_map_offset_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file_priv); > + > +/* get buffer information to memory region allocated by gem. */ > +int rockchip_gem_get_ioctl(struct drm_device *dev, void *data, > + struct drm_file *file_priv); > +#endif /* _ROCKCHIP_DRM_GEM_H */ > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c > new file mode 100644 > index 0000000..d2ec4d5 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c > @@ -0,0 +1,1372 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * 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. > + */ > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > + > +#include