From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753053AbaIXJcN (ORCPT ); Wed, 24 Sep 2014 05:32:13 -0400 Received: from regular1.263xmail.com ([211.150.99.131]:41530 "EHLO regular1.263xmail.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752661AbaIXJbz (ORCPT ); Wed, 24 Sep 2014 05:31:55 -0400 X-263anti-spam: KSV:0; X-MAIL-GRAY: 0 X-MAIL-DELIVERY: 1 X-KSVirus-check: 0 X-ABS-CHECKED: 4 X-RL-SENDER: mark.yao@rock-chips.com X-FST-TO: heiko@sntech.de X-SENDER-IP: 58.22.7.114 X-LOGIN-NAME: mark.yao@rock-chips.com X-UNIQUE-TAG: <04786d7ff586cdfb23c0ce67422e7e61> X-ATTACHMENT-NUM: 0 X-DNS-TYPE: 0 Message-ID: <54228F7B.2050608@rock-chips.com> Date: Wed, 24 Sep 2014 17:31:39 +0800 From: Mark yao User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.2.0 MIME-Version: 1.0 To: 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, daniel@ffwll.ch Subject: Re: [PATCH v4 1/5] drm/rockchip: Add basic drm driver References: <1411382820-1615-1-git-send-email-mark.yao@rock-chips.com> <1411382934-1763-1-git-send-email-mark.yao@rock-chips.com> <20140924082037.GJ15734@phenom.ffwll.local> In-Reply-To: <20140924082037.GJ15734@phenom.ffwll.local> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 2014年09月24日 16:20, Daniel Vetter wrote: > 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 > Hi, Daniel this version is old, newest is v5. and I remove uapi at v5. you can see v5 patch at: https://lkml.org/lkml/2014/9/23/1061 thanks -Mark >> 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