From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mark yao Subject: Re: [PATCH v10 1/3] drm: rockchip: Add basic drm driver Date: Wed, 08 Oct 2014 17:29:43 +0800 Message-ID: <54350407.3050905@rock-chips.com> References: <1412742179-32761-1-git-send-email-mark.yao@rock-chips.com> <1412742228-355-1-git-send-email-mark.yao@rock-chips.com> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: QUOTED-PRINTABLE Return-path: In-Reply-To: <1412742228-355-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org> Sender: linux-api-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org To: heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org, Boris BREZILLON , David Airlie , Rob Clark , Daniel Vetter , Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Randy Dunlap , Grant Likely , Greg Kroah-Hartman , John Stultz , Rom Lemarchand Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org, linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org, dianders-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org, marcheu-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org, dbehr-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org, olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org, djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org, xjq-TNX95d0MmH7DzftRWevZcw@public.gmane.org, kfx-TNX95d0MmH7DzftRWevZcw@public.gmane.org, cym-TNX95d0MmH7DzftRWevZcw@public.gmane.org, cf-TNX95d0MmH7DzftRWevZcw@public.gmane.org, zyw-TNX95d0MmH7DzftRWevZcw@public.gmane.org, xxm-TNX95d0MmH7DzftRWevZcw@public.gmane.org, huangtao-TNX95d0MmH7DzftRWevZcw@public.gmane.org, kever.yang-TNX95d0MmH7DzftRWevZcw@public.gmane.org, yxj-TNX95d0MmH7DzftRWevZcw@public.gmane.org, wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org, xw-TNX95d0MmH7DzftRWevZcw@public.gmane.org, Mark yao List-Id: linux-api@vger.kernel.org On 2014=E5=B9=B410=E6=9C=8808=E6=97=A5 12:23, Mark Yao wrote: > From: Mark yao > > This patch adds the basic structure of a DRM Driver for Rockchip Socs= =2E > > Signed-off-by: Mark Yao > Signed-off-by: Daniel Kurtz > Acked-by: Daniel Vetter > Reviewed-by: Rob Clark > --- > 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 > > Changes in v5: > Adviced by Arnd Bergmann > - doing DMA start with a 32-bit masks with dma_mask and dma_coherent_= mark > - fix some incorrect dependencies. > Adviced by Boris BREZILLON > - fix some mistake and bugs. > Adviced by Daniel Vetter > - drop all special ioctl and use generic kms ioctl instead. > Adviced by Rob Clark > - use unlocked api for drm_fb_helper_restore_fbdev_mode. > - remove unused rockchip_gem_prime_import_sg_table. > > Changes in v6: > - set gem buffer pitch 64 bytes align, needed by mali gpu. > Adviced by Daniel Kurtz > - fix some mistake, bugs, remove unused define, more better code styl= e etc. > - use clk_prepare()/unprepare() at probe()/remove() and clk_enable()/= disable() > at runtime instead of clk_prepare_enable(). > - provide a help function from vop for encoder to do mode config, ins= tead of > using drm_diaplay_mode private method. > - change vop mode_set timing to make it more safely. > > Changes in v7: > - fix memory leakage problem > > Changes in v8: > - fix iommu crash when use dual crtc. > - use frame start interrupt for vsync instead of line flag interrupt, > because the win config take affect at frame start time, if we use lin= g flag > interrupt, the address check often failed. > Adviced by Daniel Kurtz > - fix some bugs, mistake, remove unused function > - keep clock and vop disabled when probe end > - use drm_plane_helper_check_update to check update_plane if vaild > > Changes in v9: > - fix suspend and resume bug, make iommu attach and detach safely. > > Changes in v10: > Adviced by Andrzej Hajda > - check drm_dev if it's NULL at PM suspend/resume > Adviced by Sean Paul > - use drm_fb_helper_prepare to init fb_helper funcs > - Optimized code structure and remove some unnecessary Variables. > > drivers/gpu/drm/Kconfig | 2 + > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/rockchip/Kconfig | 17 + > drivers/gpu/drm/rockchip/Makefile | 8 + > drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 465 ++++++++ > drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 55 + > drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 200 ++++ > drivers/gpu/drm/rockchip/rockchip_drm_fb.h | 28 + > drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 210 ++++ > drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 20 + > drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 294 +++++ > drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 54 + > drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1415 ++++++++++++++= +++++++++++ > drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 196 ++++ > 14 files changed, 2965 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 > > 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/rockc= hip/Kconfig > new file mode 100644 > index 0000000..0ff6682 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/Kconfig > @@ -0,0 +1,17 @@ > +config DRM_ROCKCHIP > + tristate "DRM Support for Rockchip" > + depends on DRM && ROCKCHIP_IOMMU && ARM_DMA_USE_IOMMU && 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 provide > + 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/rock= chip/Makefile > new file mode 100644 > index 0000000..b3a5193 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/Makefile > @@ -0,0 +1,8 @@ > +# > +# Makefile for the drm device driver. This driver provides support = for the > +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. > + > +rockchipdrm-y :=3D rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm= _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/gp= u/drm/rockchip/rockchip_drm_drv.c > new file mode 100644 > index 0000000..480b6ba > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c > @@ -0,0 +1,465 @@ > +/* > + * 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 Publ= ic > + * 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 "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 ma= ster 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.mappin= g; > + int ret; > + > + ret =3D dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); > + if (ret) > + return ret; > + > + dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); > + > + 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(dev); > +} > + > +static int rockchip_drm_load(struct drm_device *drm_dev, unsigned lo= ng 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_KERNEL= ); > + if (!private) > + return -ENOMEM; > + > + 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, 0x00000000= , > + SZ_2G); > + if (IS_ERR(mapping)) { > + ret =3D PTR_ERR(mapping); > + goto err_config_cleanup; > + } > + > + ret =3D dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); > + if (ret) > + goto err_release_mapping; > + > + dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); > + > + 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 d= isabled > + * 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); > + > + 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; > + 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; > + > + return 0; > +} > + > +void rockchip_drm_lastclose(struct drm_device *dev) > +{ > + struct rockchip_drm_private *priv =3D dev->dev_private; > + > + drm_fb_helper_restore_fbdev_mode_unlocked(&priv->fbdev_helper); > +} > + > +static const struct file_operations rockchip_drm_driver_fops =3D { > + .owner =3D THIS_MODULE, > + .open =3D drm_open, > + .mmap =3D rockchip_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, > + .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_vmap =3D rockchip_gem_prime_vmap, > + .gem_prime_vunmap =3D rockchip_gem_prime_vunmap, > + .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); > + struct drm_connector *connector; > + > + if (pm_runtime_suspended(dev) || !drm_dev) > + return 0; > + > + drm_modeset_lock_all(dev); Oh, sorry, I make a mistake here, the drm_modeset_lock_all(...) need=20 drm_dev not dev; > + list_for_each_entry(connector, &dev->mode_config.connector_list, he= ad) { > + 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_sys_resume(struct device *dev) > +{ > + struct drm_device *drm_dev =3D dev_get_drvdata(dev); > + struct drm_connector *connector; > + > + if (!pm_runtime_suspended(dev) || !drm_dev) > + return 0; > + > + drm_modeset_lock_all(dev); > + list_for_each_entry(connector, &dev->mode_config.connector_list, he= ad) { > + if (connector->funcs->dpms) > + connector->funcs->dpms(connector, connector->dpms); > + } > + drm_modeset_unlock_all(dev); > + > + drm_helper_resume_force_mode(dev); > + > + return 0; > +} > +#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) > +}; > + > +struct drm_crtc *rockchip_drm_find_crtc(struct drm_device *drm, int = pipe) > +{ > + struct drm_crtc *c, *crtc =3D NULL; > + int i; > + > + mutex_lock(&drm->mode_config.mutex); > + list_for_each_entry(c, &drm->mode_config.crtc_list, head) > + if (i++ =3D=3D pipe) { > + crtc =3D c; > + break; > + } > + mutex_unlock(&drm->mode_config.mutex); > + > + return crtc; > +} > + > +/* > + * @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 ?: 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); > + dev_set_drvdata(dev, NULL); > +} > + > +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; > + > + 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; > + > + if (!of_device_is_available(port->parent)) { > + of_node_put(port); > + continue; > + } > + > + 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; > + > + if (!of_device_is_available(port->parent)) { > + of_node_put(port); > + continue; > + } > + > + rockchip_add_endpoints(dev, &match, port); > + of_node_put(port); > + } > + > + return component_master_add_with_match(dev, &rockchip_drm_ops, matc= h); > +} > + > +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[] =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, > + .pm =3D &rockchip_drm_pm_ops, > + }, > +}; > + > +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/gp= u/drm/rockchip/rockchip_drm_drv.h > new file mode 100644 > index 0000000..9fa82e2 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h > @@ -0,0 +1,55 @@ > +/* > + * 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 Publ= ic > + * 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 > +#include > + > +#define ROCKCHIP_MAX_FB_BUFFER 3 > +#define ROCKCHIP_MAX_CONNECTOR 2 > +#define ROCKCHIP_MAX_CRTC 2 > + > +struct drm_device; > +struct drm_connector; > + > +/* > + * Rockchip drm private structure. > + * > + * @crtc: array of enabled CRTCs, used to map from "pipe" to drm_crt= c. > + * @num_pipe: number of pipes for this device. > + */ > +struct rockchip_drm_private { > + struct drm_fb_helper fbdev_helper; > + struct drm_gem_object *fbdev_bo; > +}; > + > +struct drm_crtc *rockchip_drm_find_crtc(struct drm_device *drm, int = pipe); > +int rockchip_drm_encoder_get_mux_id(struct device_node *node, > + struct drm_encoder *encoder); > +int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc, int connect= or_type, > + int out_mode); > +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe= ); > +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pi= pe); > +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..88e43c4 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c > @@ -0,0 +1,200 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Publ= ic > + * 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_framebuffe= r *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 *m= ode_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 *fil= e_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->fbdev_helper; > + > + drm_fb_helper_hotplug_event(fb_helper); > +} > + > +static const struct drm_mode_config_funcs rockchip_drm_mode_config_f= uncs =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/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 Publ= ic > + * 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_framebuffe= r *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..363db1e > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c > @@ -0,0 +1,210 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Publ= ic > + * 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_drm_private(x) \ > + container_of(x, struct rockchip_drm_private, fbdev_helper) > + > +static int rockchip_fbdev_mmap(struct fb_info *info, > + struct vm_area_struct *vma) > +{ > + struct drm_fb_helper *helper =3D info->par; > + struct rockchip_drm_private *private =3D to_drm_private(helper); > + > + return rockchip_gem_mmap_buf(private->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_drm_private *private =3D to_drm_private(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; > + > + private->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, > + private->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 drm_fb_helper *helper; > + unsigned int num_crtc; > + int ret; > + > + if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) > + return -EINVAL; > + > + num_crtc =3D dev->mode_config.num_crtc; > + > + helper =3D &private->fbdev_helper; > + > + drm_fb_helper_prepare(dev, helper, &rockchip_drm_fb_helper_funcs); > + > + ret =3D drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONN= ECTOR); > + if (ret < 0) { > + dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n", > + ret); > + return ret; > + } > + > + ret =3D drm_fb_helper_single_add_all_connectors(helper); > + if (ret < 0) { > + dev_err(dev->dev, "Failed to add connectors - %d.\n", ret); > + 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 config - %d.\n", > + ret); > + goto err_drm_fb_helper_fini; > + } > + > + return 0; > + > +err_drm_fb_helper_fini: > + drm_fb_helper_fini(helper); > + 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; > + > + helper =3D &private->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() - %d\n", > + ret); > + > + 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); > +} > 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 Publ= ic > + * 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/gp= u/drm/rockchip/rockchip_drm_gem.c > new file mode 100644 > index 0000000..e55f0b9 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c > @@ -0,0 +1,294 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Publ= ic > + * 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 =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_obj= ) > +{ > + 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_add= r, > + &rk_obj->dma_attrs); > +} > + > +int rockchip_gem_mmap_buf(struct drm_gem_object *obj, > + struct vm_area_struct *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_ad= dr, > + obj->size, &rk_obj->dma_attrs); > +} > + > +/* drm driver mmap file operations */ > +int rockchip_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_buf(obj, vma); > + > + mutex_unlock(&dev->struct_mutex); > + > + return ret; > +} > + > +struct rockchip_gem_object * > + rockchip_gem_create_object(struct drm_device *drm, unsigned int siz= e) > +{ > + struct rockchip_gem_object *rk_obj; > + struct drm_gem_object *obj; > + 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); > + > + ret =3D rockchip_gem_alloc_buf(rk_obj); > + if (ret) > + goto err_free_rk_obj; > + > + return rk_obj; > + > +err_free_rk_obj: > + kfree(rk_obj); > + return ERR_PTR(ret); > +} > + > +/* > + * rockchip_gem_free_object - (struct drm_driver)->gem_free_object c= allback > + * 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); > + > + kfree(rk_obj); > +} > + > +/* > + * rockchip_gem_create_with_handle - allocate an object with the giv= en > + * size and create a gem handle on it > + * > + * returns a struct rockchip_gem_object* on success or ERR_PTR value= s > + * 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 ERR_CAST(rk_obj); > + > + 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; > + > + mutex_lock(&dev->struct_mutex); > + > + 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%llx\n", *offset); > + > +out: > + drm_gem_object_unreference(obj); > +unlock: > + mutex_unlock(&dev->struct_mutex); > + return ret; > +} > + > +/* > + * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callb= ack > + * 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 =3D DIV_ROUND_UP(args->width * args->bpp, 8); > + > + /* > + * align to 64 bytes since Mali requires it. > + */ > + min_pitch =3D ALIGN(min_pitch, 64); > + > + 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->si= ze, > + &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_obje= ct *obj) > +{ > + struct rockchip_gem_object *rk_obj =3D to_rockchip_obj(obj); > + struct drm_device *drm =3D obj->dev; > + struct sg_table *sgt; > + 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; > +} > + > +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 *vad= dr) > +{ > + /* Nothing to do */ > +} > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gp= u/drm/rockchip/rockchip_drm_gem.h > new file mode 100644 > index 0000000..67bcebe > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h > @@ -0,0 +1,54 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Publ= ic > + * 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_objec= t, 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_obje= ct *obj); > +struct drm_gem_object * > +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t si= ze, > + 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 *vad= dr); > + > +/* drm driver mmap file operations */ > +int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma)= ; > + > +/* mmap a gem object to userspace. */ > +int rockchip_gem_mmap_buf(struct drm_gem_object *obj, > + struct vm_area_struct *vma); > + > +struct rockchip_gem_object * > + rockchip_gem_create_object(struct drm_device *drm, unsigned int siz= e); > + > +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); > +#endif /* _ROCKCHIP_DRM_GEM_H */ > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gp= u/drm/rockchip/rockchip_drm_vop.c > new file mode 100644 > index 0000000..b49b6b4 > --- /dev/null > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c > @@ -0,0 +1,1415 @@ > +/* > + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd > + * Author:Mark Yao > + * > + * This software is licensed under the terms of the GNU General Publ= ic > + * 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 > +#include > + > +#include