From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mark yao Subject: Re: [PATCH v4 1/5] drm/rockchip: Add basic drm driver Date: Wed, 24 Sep 2014 17:31:39 +0800 Message-ID: <54228F7B.2050608@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> <20140924082037.GJ15734@phenom.ffwll.local> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: QUOTED-PRINTABLE Return-path: In-Reply-To: <20140924082037.GJ15734-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org> Sender: devicetree-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org To: heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org, 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-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, kever.yang-TNX95d0MmH7DzftRWevZcw@public.gmane.org, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org, dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org, xjq-TNX95d0MmH7DzftRWevZcw@public.gmane.org, zyw-TNX95d0MmH7DzftRWevZcw@public.gmane.org, cym-TNX95d0MmH7DzftRWevZcw@public.gmane.org, linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org, kfx-TNX95d0MmH7DzftRWevZcw@public.gmane.org, wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org, huangtao-TNX95d0MmH7DzftRWevZcw@public.gmane.org, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, yxj-TNX95d0MmH7DzftRWevZcw@public.gmane.org, marcheu-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org, xxm@rock-chi List-Id: devicetree@vger.kernel.org On 2014=E5=B9=B409=E6=9C=8824=E6=97=A5 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 Soc= s. >> >> 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 se= t,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 >> =20 >> source "drivers/gpu/drm/exynos/Kconfig" >> =20 >> +source "drivers/gpu/drm/rockchip/Kconfig" >> + >> source "drivers/gpu/drm/vmwgfx/Kconfig" >> =20 >> 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)+=3D vmwgfx/ >> obj-$(CONFIG_DRM_VIA) +=3Dvia/ >> obj-$(CONFIG_DRM_NOUVEAU) +=3Dnouveau/ >> obj-$(CONFIG_DRM_EXYNOS) +=3Dexynos/ >> +obj-$(CONFIG_DRM_ROCKCHIP) +=3Drockchip/ >> obj-$(CONFIG_DRM_GMA500) +=3D gma500/ >> obj-$(CONFIG_DRM_UDL) +=3D udl/ >> obj-$(CONFIG_DRM_AST) +=3D ast/ >> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rock= chip/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/roc= kchip/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= =2E >> + >> +ccflags-y :=3D -Iinclude/drm -Idrivers/gpu/drm/rockchip >> + >> +rockchipdrm-y :=3D rockchip_drm_drv.o rockchip_drm_fb.o rockchip_dr= m_fbdev.o \ >> + rockchip_drm_gem.o rockchip_drm_vop.o >> + >> +obj-$(CONFIG_DRM_ROCKCHIP) +=3D rockchipdrm.o >> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/g= pu/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 Pub= lic >> + * 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 m= aster 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 =3D drm_dev->dev->archdata.mappi= ng; >> + int ret; >> + >> + ret =3D 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 l= ong flags) >> +{ >> + struct rockchip_drm_private *private; >> + struct dma_iommu_mapping *mapping; >> + struct device *dev =3D drm_dev->dev; >> + int ret; >> + >> + private =3D devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNE= L); >> + if (!private) >> + return -ENOMEM; >> + >> + dev_set_drvdata(drm_dev->dev, dev); >> + drm_dev->dev_private =3D private; >> + >> + drm_mode_config_init(drm_dev); >> + >> + rockchip_drm_mode_config_init(drm_dev); >> + >> + dev->dma_parms =3D devm_kzalloc(dev, sizeof(*dev->dma_parms), >> + GFP_KERNEL); >> + if (!dev->dma_parms) { >> + ret =3D -ENOMEM; >> + goto err_config_cleanup; >> + } >> + >> + /* TODO(djkurtz): fetch the mapping start/size from somewhere */ >> + mapping =3D arm_iommu_create_mapping(&platform_bus_type, 0x1000000= 0, >> + SZ_1G); >> + if (IS_ERR(mapping)) { >> + ret =3D PTR_ERR(mapping); >> + goto err_config_cleanup; >> + } >> + >> + dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); >> + dma_set_max_seg_size(dev, 0xffffffffu); >> + >> + ret =3D arm_iommu_attach_device(dev, mapping); >> + if (ret) >> + goto err_release_mapping; >> + >> + /* Try to bind all sub drivers. */ >> + ret =3D 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 =3D true, we can use the vblank feature. >> + */ >> + drm_dev->irq_enabled =3D true; >> + >> + /* >> + * with vblank_disable_allowed =3D 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 =3D true; >> + >> + ret =3D 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 =3D NULL; >> + dev_set_drvdata(dev, NULL); >> + return ret; >> +} >> + >> +static int rockchip_drm_unload(struct drm_device *drm_dev) >> +{ >> + struct device *dev =3D 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 =3D 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, h= ead) { >> + int old_dpms =3D 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 =3D 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, h= ead) { >> + 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 =3D 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[] =3D { >> + 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 =3D { >> + .owner =3D THIS_MODULE, >> + .open =3D drm_open, >> + .mmap =3D rockchip_drm_gem_mmap, >> + .poll =3D drm_poll, >> + .read =3D drm_read, >> + .unlocked_ioctl =3D drm_ioctl, >> +#ifdef CONFIG_COMPAT >> + .compat_ioctl =3D drm_compat_ioctl, >> +#endif >> + .release =3D drm_release, >> +}; >> + >> +const struct vm_operations_struct rockchip_drm_vm_ops =3D { >> + .open =3D drm_gem_vm_open, >> + .close =3D drm_gem_vm_close, >> +}; >> + >> +static struct drm_driver rockchip_drm_driver =3D { >> + .driver_features =3D DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, >> + .load =3D rockchip_drm_load, >> + .unload =3D rockchip_drm_unload, >> + .lastclose =3D rockchip_drm_lastclose, >> + .suspend =3D rockchip_drm_suspend, >> + .resume =3D rockchip_drm_resume, >> + .get_vblank_counter =3D drm_vblank_count, >> + .enable_vblank =3D rockchip_drm_crtc_enable_vblank, >> + .disable_vblank =3D rockchip_drm_crtc_disable_vblank, >> + .gem_vm_ops =3D &rockchip_drm_vm_ops, >> + .gem_free_object =3D rockchip_gem_free_object, >> + .dumb_create =3D rockchip_gem_dumb_create, >> + .dumb_map_offset =3D rockchip_gem_dumb_map_offset, >> + .dumb_destroy =3D drm_gem_dumb_destroy, >> + .prime_handle_to_fd =3D drm_gem_prime_handle_to_fd, >> + .prime_fd_to_handle =3D drm_gem_prime_fd_to_handle, >> + .gem_prime_import =3D drm_gem_prime_import, >> + .gem_prime_export =3D drm_gem_prime_export, >> + .gem_prime_get_sg_table =3D rockchip_gem_prime_get_sg_table, >> + .gem_prime_import_sg_table =3D rockchip_gem_prime_import_sg_table, >> + .gem_prime_vmap =3D rockchip_gem_prime_vmap, >> + .gem_prime_vunmap =3D rockchip_gem_prime_vunmap, >> + .gem_prime_mmap =3D rockchip_gem_prime_mmap, >> + .ioctls =3D rockchip_ioctls, >> + .num_ioctls =3D ARRAY_SIZE(rockchip_ioctls), >> + .fops =3D &rockchip_drm_driver_fops, >> + .name =3D DRIVER_NAME, >> + .desc =3D DRIVER_DESC, >> + .date =3D DRIVER_DATE, >> + .major =3D DRIVER_MAJOR, >> + .minor =3D DRIVER_MINOR, >> +}; >> + >> +#ifdef CONFIG_PM_SLEEP >> +static int rockchip_drm_sys_suspend(struct device *dev) >> +{ >> + struct drm_device *drm_dev =3D dev_get_drvdata(dev); >> + pm_message_t message; >> + >> + if (pm_runtime_suspended(dev)) >> + return 0; >> + >> + message.event =3D PM_EVENT_SUSPEND; >> + >> + return rockchip_drm_suspend(drm_dev, message); >> +} >> + >> +static int rockchip_drm_sys_resume(struct device *dev) >> +{ >> + struct drm_device *drm_dev =3D 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 =3D { >> + 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 =3D drm->dev_private; >> + struct device_node *port; >> + int pipe; >> + >> + if (priv->num_pipe >=3D ROCKCHIP_MAX_CRTC) >> + return -EINVAL; >> + >> + port =3D 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 =3D priv->num_pipe++; >> + crtc->port =3D port; >> + >> + priv->crtc[pipe] =3D crtc; >> + >> + return pipe; >> +} >> + >> +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe) >> +{ >> + struct rockchip_drm_private *priv =3D drm->dev_private; >> + >> + priv->num_pipe--; >> + of_node_put(priv->crtc[pipe]->port); >> + priv->crtc[pipe] =3D NULL; >> +} >> + >> +struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pip= e) >> +{ >> + struct rockchip_drm_private *priv =3D 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 =3D NULL; >> + struct drm_crtc *crtc =3D encoder->crtc; >> + struct of_endpoint endpoint; >> + struct device_node *port; >> + int ret; >> + >> + if (!node || !crtc) >> + return -EINVAL; >> + >> + do { >> + ep =3D of_graph_get_next_endpoint(node, ep); >> + if (!ep) >> + break; >> + >> + port =3D of_graph_get_remote_port(ep); >> + of_node_put(port); >> + if (port =3D=3D crtc->port) { >> + ret =3D 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 =3D data; >> + >> + return dev->of_node =3D=3D 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 =3D 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 =3D drm_dev_alloc(&rockchip_drm_driver, dev); >> + if (!drm) >> + return -ENOMEM; >> + >> + ret =3D drm_dev_set_unique(drm, "%s", dev_name(dev)); >> + if (ret) >> + goto err_free; >> + >> + ret =3D 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 =3D dev_get_drvdata(dev); >> + >> + drm_dev_unregister(drm); >> + drm_dev_unref(drm); >> +} >> + >> +static const struct component_master_ops rockchip_drm_ops =3D { >> + .bind =3D rockchip_drm_bind, >> + .unbind =3D rockchip_drm_unbind, >> +}; >> + >> +static int rockchip_drm_platform_probe(struct platform_device *pdev= ) >> +{ >> + struct device *dev =3D &pdev->dev; >> + struct component_match *match =3D NULL; >> + struct device_node *np =3D 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 =3D 0;; i++) { >> + port =3D of_parse_phandle(np, "ports", i); >> + if (!port) >> + break; >> + >> + component_match_add(dev, &match, compare_of, port->parent); >> + of_node_put(port); >> + } >> + >> + if (i =3D=3D 0) { >> + dev_err(dev, "missing 'ports' property\n"); >> + return -ENODEV; >> + } >> + /* >> + * For each bound crtc, bind the encoders attached to its >> + * remote endpoint. >> + */ >> + for (i =3D 0;; i++) { >> + port =3D of_parse_phandle(np, "ports", i); >> + if (!port) >> + break; >> + >> + rockchip_add_endpoints(dev, &match, port); >> + of_node_put(port); >> + } >> + >> + ret =3D dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); >> + if (ret) >> + return ret; >> + >> + return component_master_add_with_match(dev, &rockchip_drm_ops, mat= ch); >> +} >> + >> +static int rockchip_drm_platform_remove(struct platform_device *pde= v) >> +{ >> + component_master_del(&pdev->dev, &rockchip_drm_ops); >> + return 0; >> +} >> + >> +static const struct of_device_id rockchip_drm_dt_ids[] =3D { >> + { .compatible =3D "rockchip,display-subsystem", }, >> + { /* sentinel */ }, >> +}; >> +MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids); >> + >> +static struct platform_driver rockchip_drm_platform_driver =3D { >> + .probe =3D rockchip_drm_platform_probe, >> + .remove =3D rockchip_drm_platform_remove, >> + .driver =3D { >> + .owner =3D THIS_MODULE, >> + .name =3D "rockchip-drm", >> + .of_match_table =3D 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/g= pu/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 Pub= lic >> + * 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 =3D 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 vbl= ank. >> + */ >> + 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 pip= e); >> +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 pip= e); >> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int p= ipe); >> +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/gp= u/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 Pub= lic >> + * 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, f= b) >> + >> +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_framebuff= er *fb, >> + unsigned int plane) >> +{ >> + struct rockchip_drm_fb *rk_fb =3D to_rockchip_fb(fb); >> + >> + if (plane >=3D 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 =3D to_rockchip_fb(fb); >> + struct drm_gem_object *obj; >> + int i; >> + >> + for (i =3D 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) { >> + obj =3D 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 =3D 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 =3D { >> + .destroy =3D rockchip_drm_fb_destroy, >> + .create_handle =3D 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 =3D 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 =3D 0; i < num_planes; i++) >> + rockchip_fb->obj[i] =3D obj[i]; >> + >> + ret =3D 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 *fi= le_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 =3D drm_format_horz_chroma_subsampling(mode_cmd->pixel_format= ); >> + vsub =3D drm_format_vert_chroma_subsampling(mode_cmd->pixel_format= ); >> + num_planes =3D min(drm_format_num_planes(mode_cmd->pixel_format), >> + ROCKCHIP_MAX_FB_BUFFER); >> + >> + for (i =3D 0; i < num_planes; i++) { >> + unsigned int width =3D mode_cmd->width / (i ? hsub : 1); >> + unsigned int height =3D mode_cmd->height / (i ? vsub : 1); >> + unsigned int min_size; >> + >> + obj =3D drm_gem_object_lookup(dev, file_priv, >> + mode_cmd->handles[i]); >> + if (!obj) { >> + dev_err(dev->dev, "Failed to lookup GEM object\n"); >> + ret =3D -ENXIO; >> + goto err_gem_object_unreference; >> + } >> + >> + min_size =3D (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 =3D -EINVAL; >> + goto err_gem_object_unreference; >> + } >> + objs[i] =3D obj; >> + } >> + >> + rockchip_fb =3D rockchip_fb_alloc(dev, mode_cmd, objs, i); >> + if (IS_ERR(rockchip_fb)) { >> + ret =3D PTR_ERR(rockchip_fb); >> + goto err_gem_object_unreference; >> + } >> + >> + return &rockchip_fb->fb; >> + >> +err_gem_object_unreference: >> + for (i--; i >=3D 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 =3D dev->dev_private; >> + struct drm_fb_helper *fb_helper =3D 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 =3D { >> + .fb_create =3D rockchip_user_fb_create, >> + .output_poll_changed =3D 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 =3D 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 =3D 0; >> + dev->mode_config.min_height =3D 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 =3D 4096; >> + dev->mode_config.max_height =3D 4096; >> + >> + dev->mode_config.funcs =3D &rockchip_drm_mode_config_funcs; >> +} >> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gp= u/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 Pub= lic >> + * 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_framebuff= er *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 Pub= lic >> + * 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 =3D info->par; >> + struct rockchip_fbdev *fbdev =3D to_rockchip_fbdev(helper); >> + >> + return rockchip_gem_mmap(fbdev->bo, vma); >> +} >> + >> +static struct fb_ops rockchip_drm_fbdev_ops =3D { >> + .owner =3D THIS_MODULE, >> + .fb_mmap =3D rockchip_fbdev_mmap, >> + .fb_fillrect =3D cfb_fillrect, >> + .fb_copyarea =3D cfb_copyarea, >> + .fb_imageblit =3D cfb_imageblit, >> + .fb_check_var =3D drm_fb_helper_check_var, >> + .fb_set_par =3D drm_fb_helper_set_par, >> + .fb_blank =3D drm_fb_helper_blank, >> + .fb_pan_display =3D drm_fb_helper_pan_display, >> + .fb_setcmap =3D 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 =3D to_rockchip_fbdev(helper); >> + struct drm_mode_fb_cmd2 mode_cmd =3D { 0 }; >> + struct drm_device *dev =3D 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 =3D DIV_ROUND_UP(sizes->surface_bpp, 8); >> + >> + mode_cmd.width =3D sizes->surface_width; >> + mode_cmd.height =3D sizes->surface_height; >> + mode_cmd.pitches[0] =3D sizes->surface_width * bytes_per_pixel; >> + mode_cmd.pixel_format =3D drm_mode_legacy_fb_format(sizes->surface= _bpp, >> + sizes->surface_depth); >> + >> + size =3D mode_cmd.pitches[0] * mode_cmd.height; >> + >> + rk_obj =3D rockchip_gem_create_object(dev, size); >> + if (IS_ERR(rk_obj)) >> + return -ENOMEM; >> + >> + fbdev->bo =3D &rk_obj->base; >> + >> + fbi =3D framebuffer_alloc(0, dev->dev); >> + if (!fbi) { >> + dev_err(dev->dev, "Failed to allocate framebuffer info.\n"); >> + ret =3D -ENOMEM; >> + goto err_rockchip_gem_free_object; >> + } >> + >> + helper->fb =3D 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 =3D PTR_ERR(helper->fb); >> + goto err_framebuffer_release; >> + } >> + >> + helper->fbdev =3D fbi; >> + >> + fbi->par =3D helper; >> + fbi->flags =3D FBINFO_FLAG_DEFAULT; >> + fbi->fbops =3D &rockchip_drm_fbdev_ops; >> + >> + ret =3D 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 =3D 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 =3D fbi->var.xoffset * bytes_per_pixel; >> + offset +=3D fbi->var.yoffset * fb->pitches[0]; >> + >> + dev->mode_config.fb_base =3D 0; >> + fbi->screen_base =3D rk_obj->kvaddr + offset; >> + fbi->screen_size =3D rk_obj->base.size; >> + fbi->fix.smem_len =3D rk_obj->base.size; >> + >> + DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=3D%p offset=3D%ld size=3D%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 =3D = { >> + .fb_probe =3D rockchip_drm_fbdev_create, >> +}; >> + >> +int rockchip_drm_fbdev_init(struct drm_device *dev) >> +{ >> + struct rockchip_drm_private *private =3D 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 =3D dev->mode_config.num_crtc; >> + >> + fbdev =3D kzalloc(sizeof(*fbdev), GFP_KERNEL); >> + if (!fbdev) >> + return -ENOMEM; >> + >> + fbdev->helper.funcs =3D &rockchip_drm_fb_helper_funcs; >> + helper =3D &fbdev->helper; >> + >> + ret =3D drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CON= NECTOR); >> + if (ret < 0) { >> + dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); >> + goto err_free; >> + } >> + >> + ret =3D 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 =3D 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 =3D 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 =3D dev->dev_private; >> + struct drm_fb_helper *helper; >> + struct rockchip_fbdev *fbdev; >> + >> + if (!private || !private->fb_helper) >> + return; >> + >> + helper =3D private->fb_helper; >> + fbdev =3D to_rockchip_fbdev(helper); >> + >> + if (helper->fbdev) { >> + struct fb_info *info; >> + int ret; >> + >> + info =3D helper->fbdev; >> + ret =3D 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 =3D 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 Pub= lic >> + * 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/g= pu/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 Pub= lic >> + * 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_ob= j) >> +{ >> + struct drm_gem_object *obj =3D &rk_obj->base; >> + struct drm_device *drm =3D 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 =3D 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 =3D 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_ob= j) >> +{ >> + struct drm_gem_object *obj =3D &rk_obj->base; >> + struct drm_device *drm =3D obj->dev; >> + >> + dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_ad= dr, >> + &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 =3D filp->private_data; >> + struct drm_device *dev =3D 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 =3D 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 =3D container_of(node, struct drm_gem_object, vma_node); >> + ret =3D 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 =3D filp->private_data; >> + >> + return rockchip_gem_mmap(obj, vma); >> +} >> + >> +static const struct file_operations rockchip_drm_gem_fops =3D { >> + .mmap =3D rockchip_drm_gem_mmap_buffer, >> +}; >> + >> +struct rockchip_gem_object * >> + rockchip_gem_create_object(struct drm_device *drm, unsigned int si= ze) >> +{ >> + struct rockchip_gem_object *rk_obj; >> + struct drm_gem_object *obj; >> + struct file *filp; >> + int ret; >> + >> + size =3D round_up(size, PAGE_SIZE); >> + >> + rk_obj =3D kzalloc(sizeof(*rk_obj), GFP_KERNEL); >> + if (!rk_obj) >> + return ERR_PTR(-ENOMEM); >> + >> + obj =3D &rk_obj->base; >> + >> + drm_gem_private_object_init(drm, obj, size); >> + >> + filp =3D 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 =3D PTR_ERR(filp); >> + goto err_free_rk_obj; >> + } >> + filp->f_mode =3D FMODE_READ | FMODE_WRITE; >> + obj->filp =3D filp; >> + >> + ret =3D drm_gem_create_mmap_offset(obj); >> + if (ret) >> + goto err_free_obj; >> + >> + ret =3D 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 =3D 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_st= ruct *vma) >> +{ >> + struct rockchip_gem_object *rk_obj =3D to_rockchip_obj(obj); >> + struct drm_device *drm =3D obj->dev; >> + unsigned long vm_size; >> + >> + vma->vm_flags |=3D VM_IO | VM_DONTEXPAND | VM_DONTDUMP; >> + vm_size =3D 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_a= ddr, >> + obj->size, &rk_obj->dma_attrs); >> +} >> + >> +/* >> + * rockchip_gem_create_with_handle - allocate an object with the gi= ven >> + * size and create a gem handle on it >> + * >> + * returns a struct rockchip_gem_object* on success or ERR_PTR valu= es >> + * 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 =3D rockchip_gem_create_object(drm, size); >> + if (IS_ERR(rk_obj)) >> + return NULL; >> + >> + obj =3D &rk_obj->base; >> + >> + /* >> + * allocate a id of idr table where the obj is registered >> + * and handle has the id what user can see. >> + */ >> + ret =3D 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 =3D 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 =3D drm_gem_object_lookup(dev, file_priv, handle); >> + if (!obj) { >> + DRM_ERROR("failed to lookup gem object.\n"); >> + ret =3D -EINVAL; >> + goto unlock; >> + } >> + >> + ret =3D drm_gem_create_mmap_offset(obj); >> + if (ret) >> + goto out; >> + >> + *offset =3D drm_vma_node_offset_addr(&obj->vma_node); >> + DRM_DEBUG_KMS("offset =3D 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 call= back >> + * function >> + * >> + * This aligns the pitch and size arguments to the minimum required= =2E 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 =3D DIV_ROUND_UP(args->width * args->bpp, 8); >> + >> + if (args->pitch < min_pitch) >> + args->pitch =3D min_pitch; >> + >> + if (args->size < args->pitch * args->height) >> + args->size =3D args->pitch * args->height; >> + >> + rk_obj =3D rockchip_gem_create_with_handle(file_priv, dev, args->s= ize, >> + &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 =3D data; >> + struct rockchip_gem_object *rk_obj; >> + struct drm_gem_object *obj; >> + >> + mutex_lock(&dev->struct_mutex); >> + >> + obj =3D 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 =3D to_rockchip_obj(obj); >> + >> + args->flags =3D rk_obj->flags; >> + args->size =3D 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 *dat= a, >> + struct drm_file *file_priv) >> +{ >> + struct drm_rockchip_gem_map_off *args =3D 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 =3D data; >> + struct rockchip_gem_object *rk_obj; >> + >> + rk_obj =3D rockchip_gem_create_with_handle(file_priv, dev, args->s= ize, >> + &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_obj= ect *obj) >> +{ >> + struct rockchip_gem_object *rk_obj =3D to_rockchip_obj(obj); >> + struct drm_device *drm =3D obj->dev; >> + struct sg_table *sgt =3D NULL; >> + int ret; >> + >> + sgt =3D kzalloc(sizeof(*sgt), GFP_KERNEL); >> + if (!sgt) >> + return ERR_PTR(-ENOMEM); >> + >> + ret =3D 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 s= ize, >> + struct sg_table *sgt) >> +{ >> + struct rockchip_gem_object *rk_obj; >> + >> + if (sgt->nents !=3D 1) >> + return ERR_PTR(-EINVAL); >> + >> + rk_obj =3D 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 =3D to_rockchip_obj(obj); >> + >> + return rk_obj->kvaddr; >> +} >> + >> +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *va= ddr) >> +{ >> + /* Nothing to do */ >> +} >> + >> +int rockchip_gem_prime_mmap(struct drm_gem_object *obj, >> + struct vm_area_struct *vma) >> +{ >> + struct drm_device *dev =3D obj->dev; >> + int ret; >> + >> + mutex_lock(&dev->struct_mutex); >> + ret =3D 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/g= pu/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 Pub= lic >> + * 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_obje= ct, 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_obj= ect *obj); >> +struct drm_gem_object * >> +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t s= ize, >> + 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 *va= ddr); >> +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_st= ruct *vma); >> + >> +struct rockchip_gem_object * >> + rockchip_gem_create_object(struct drm_device *drm, unsigned int si= ze); >> + >> +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 *dat= a, >> + 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 *dat= a, >> + 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/g= pu/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 Pub= lic >> + * 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