From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mark yao Subject: Re: [PATCH v5 1/3] drm/rockchip: Add basic drm driver Date: Thu, 25 Sep 2014 16:58:57 +0800 Message-ID: <5423D951.4030703@rock-chips.com> References: <1411524672-14524-1-git-send-email-mark.yao@rock-chips.com> <1411524746-14635-1-git-send-email-mark.yao@rock-chips.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="===============0892311294==" Return-path: In-Reply-To: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" To: Daniel Kurtz Cc: Mark Rutland , =?UTF-8?B?SGVpa28gU3TDvGJuZXI=?= , linux-doc@vger.kernel.org, kever.yang@rock-chips.com, dri-devel , "linux-kernel@vger.kernel.org" , xjq@rock-chips.com, zyw@rock-chips.com, linux-api@vger.kernel.org, jeff chen , linux-rockchip@lists.infradead.org, kfx@rock-chips.com, Grant Likely , wxt@rock-chips.com, huangtao@rock-chips.com, devicetree@vger.kernel.org, Pawel Moll , Ian Campbell , yxj@rock-chips.com, Eddie Cai , Rob Herring , =?UTF-8?B?U3TDqXBoYW5lIE1hcmNoZXNpbg==?= , simon xue , xw@rock-chips.com, Greg Kroah-Hartman , Randy Dunlap List-Id: linux-api@vger.kernel.org This is a multi-part message in MIME format. --===============0892311294== Content-Type: multipart/alternative; boundary="------------020302040903060005080105" This is a multi-part message in MIME format. --------------020302040903060005080105 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: quoted-printable On 2014=E5=B9=B409=E6=9C=8825=E6=97=A5 00:20, Daniel Kurtz wrote: > Hi Mark, > > Please review comments inline... > > On Wed, Sep 24, 2014 at 10:12 AM, Mark yao wr= ote: >> This patch adds the basic structure of a DRM Driver for Rockchip Socs. >> >> Signed-off-by: Mark yao >> --- >> Changes in v2: >> - use the component framework to defer main drm driver probe >> until all VOP devices have been probed. >> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by >> master device and each vop device can shared the drm dma mapping. >> - use drm_crtc_init_with_planes and drm_universal_plane_init. >> - remove unnecessary middle layers. >> - add cursor set, move funcs to rockchip drm crtc. >> - use vop reset at first init >> - reference framebuffer when used and unreference when swap out vop >> >> Changes in v3: >> - change "crtc->fb" to "crtc->primary-fb" >> Adviced by Daniel Vetter >> - init cursor plane with universal api, remove unnecessary cursor set,= move >> >> Changes in v4: >> Adviced by David Herrmann >> - remove drm_platform_*() usage, use register drm device directly. >> Adviced by Rob Clark >> - remove special mmap ioctl, do userspace mmap with normal mmap() or m= map offset >> >> Changes in v5: >> Adviced by Arnd Bergmann >> - doing DMA start with a 32-bit masks with dma_mask and dma_coherent_m= ark >> - 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. >> >> 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 | 509 +++++++++ >> 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 | 230 +++++ >> drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 20 + >> drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 341 ++++++ >> drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 55 + >> drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1373 +++++++++++++++= ++++++++++ >> drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 187 ++++ >> 14 files changed, 3092 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 >> >> source "drivers/gpu/drm/exynos/Kconfig" >> >> +source "drivers/gpu/drm/rockchip/Kconfig" >> + >> source "drivers/gpu/drm/vmwgfx/Kconfig" >> >> source "drivers/gpu/drm/gma500/Kconfig" >> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile >> index 4a55d59..d03387a 100644 >> --- a/drivers/gpu/drm/Makefile >> +++ b/drivers/gpu/drm/Makefile >> @@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_VMWGFX)+=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/rockch= ip/Kconfig >> new file mode 100644 >> index 0000000..87255f7 >> --- /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 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/rockc= hip/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 f= or 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/gpu= /drm/rockchip/rockchip_drm_drv.c >> new file mode 100644 >> index 0000000..0ca4a6b >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c >> @@ -0,0 +1,509 @@ >> +/* >> + * 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 Publi= c >> + * License version 2, as published by the Free Software Foundation, a= nd >> + * 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 mas= ter drm >> + * device. This is used by the VOPs to map GEM buffers to a common D= MA >> + * 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.m= apping; >> + int ret; >> + >> + ret =3D dma_set_mask_and_coherent(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 lon= g 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_K= ERNEL); >> + 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, 0x100= 00000, >> + SZ_1G); >> + 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_config_cleanup; > goto err_release_mapping; oh, I make a mistake... Sure, I will fix it. > >> + >> + 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; >> + 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; >> +} >> + >> +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_lis= t, head) { >> + int old_dpms =3D connector->dpms; >> + >> + if (connector->funcs->dpms) >> + connector->funcs->dpms(connector, DRM_MODE_DPM= S_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_lis= t, head) { >> + if (connector->funcs->dpms) >> + connector->funcs->dpms(connector, connector->d= pms); >> + } >> + 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; >> + >> + if (priv->fb_helper) >> + drm_fb_helper_restore_fbdev_mode_unlocked(priv->fb_hel= per); >> +} >> + >> +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 | DRIV= ER_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, > Looking at drm_sysfs.c, the .suspend/.resume callbacks are only > invoked if the driver does not set DRIVER_MODESET in .driver_features. > Since this driver does have DRIVER_MODESET, let's just drop these two i= tems. > The platform_driver provides its own suspend/resume callbacks, instead. right, driver_features with DRIVER_MODESET, the suspend/resume do not be=20 called. I will remove it to platform_driver. >> + .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, >> + .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) >> +}; > Looks like this is not actually hooked up. > Perhaps you meant to add this to rockchip_drm_platform_driver, see belo= w... > >> + >> +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *cr= tc, >> + 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"); >> + 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 pipe) >> +{ >> + 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; > nit: > > return ret ?: endpoint.id; OK, I will do it. > >> + } >> + } 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 avai= lable\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 callba= cks >> + * works as expected. >> + */ >> + for (i =3D 0;; i++) { >> + port =3D of_parse_phandle(np, "ports", i); >> + if (!port) >> + break; > I think you want to make sure that the parent node of this 'port' (ie, > the VOP node) is not disabled... > > if (!of_device_is_available(port->parent)) { > of_node_put(port); > continue; > } right, vop node maybe disabled, we should check it. >> + >> + component_match_add(dev, &match, compare_of, port->par= ent); >> + 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; >> + > Here, too: > if (!of_device_is_available(port->parent)) { > of_node_put(port); > continue; > } OK. >> + rockchip_add_endpoints(dev, &match, port); >> + of_node_put(port); >> + } >> + >> + ret =3D dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); >> + if (ret) >> + return ret; >> + >> + return component_master_add_with_match(dev, &rockchip_drm_ops,= match); >> +} >> + >> +static int rockchip_drm_platform_remove(struct platform_device *pdev) >> +{ >> + component_master_del(&pdev->dev, &rockchip_drm_ops); >> + >> + return 0; >> +} >> + >> +static const struct of_device_id rockchip_drm_dt_ids[] =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, OK >> + }, >> +}; >> + >> +module_platform_driver(rockchip_drm_platform_driver); >> + >> +MODULE_AUTHOR("Mark Yao "); >> +MODULE_DESCRIPTION("ROCKCHIP DRM Driver"); >> +MODULE_LICENSE("GPL v2"); >> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu= /drm/rockchip/rockchip_drm_drv.h >> new file mode 100644 >> index 0000000..154b3ec >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h >> @@ -0,0 +1,120 @@ >> +/* >> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >> + * Author:Mark Yao >> + * >> + * based on exynos_drm_drv.h >> + * >> + * This software is licensed under the terms of the GNU General Publi= c >> + * License version 2, as published by the Free Software Foundation, a= nd >> + * 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 > This is only used to size the array of gem object pointers used by a > framebuffer . > What framebuffer pixel format uses 4 planes? I think the max is 3? right, the max is 3 with YUV format. >> +#define ROCKCHIP_MAX_CONNECTOR 2 >> + >> +struct drm_device; >> +struct drm_connector; >> + >> +/* >> + * display output interface supported by rockchip lcdc > s/lcdc/vop OK, I will fix it. >> + */ >> +#define ROCKCHIP_OUTFACE_P888 0 >> +#define ROCKCHIP_OUTFACE_P666 1 >> +#define ROCKCHIP_OUTFACE_P565 2 >> +/* for use special outface */ >> +#define ROCKCHIP_OUTFACE_AAAA 15 > "outface" is creative. It is actually an English word that means "to > disconcert an opponent by bold confrontation", I do not think that is > what you mean here :-). > > Rename these to ROCKCHIP_OUT_MODE_, to be consistent with the > corresponding field (dsp_out_mode) of the VOP_DSP_CTRL0 register to > which these are written. > Also, since these are register values written to VOP_DSP_CTRL0, so > move them to the VOP header file. "outface" maybe Chinese English, so you are right, I will change it. >> + >> +#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, > To match the enum name, use ROCKCHIP_OUTPUT_TYPE_*. > Also, no need to explicitly set the first one to 0. > However, see below. I don't think we to modify the drm_display_mode > to include an output type. but vop devices need know the connector type, connector enable register=20 is in vop. can I do that like under to get connector type for crtc? static int rockchip_get_connector_type(struct drm_crtc *crtc) { struct drm_device *dev =3D crtc->dev; struct drm_connector * connector; list_for_each_entry(connector,=20 &dev->mode_config.connector_list, head) { if (!connector->encoder) continue; /* * one crtc only has one connector in my case, so just find=20 the first connector at list. */ if (connector->encoder->crtc =3D=3D crtc) break; } if (!connector) return -EINVAL; return connector->connector_type; } >> + /* 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, >> +}; > None of the following defined above are used and they can all be remove= d: > ROCKCHIP_EVENT_HOTPLUG > rockchip_plane_type > rockchip_drm_device_type > ROCKCHIP_COLOR_SWAP_* > rockchip_crtc_type OK, I will drop them. >> + >> +/* >> + * Rockchip drm private structure. >> + * >> + * @num_pipe: number of pipes for this device. >> + */ >> +struct rockchip_drm_private { >> + struct drm_fb_helper *fb_helper; >> + /* >> + * created crtc object would be contained at this array and >> + * this array is used to be aware of which crtc did it request= vblank. >> + */ > Move this comment up to the struct comment, above "num_pipe". > > @crtc: array of enabled CRTCs, used to map from "pipe" to drm_crtc OK >> + struct drm_crtc *crtc[ROCKCHIP_MAX_CRTC]; >> + >> + unsigned int num_pipe; >> +}; >> + >> +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *cr= tc, >> + struct device_node *port); >> +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe); >> +struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe)= ; > For consistency, this should probably be "rockchip_drm_find_crtc", > although you can probably drop the "_drm" from all of these function > names if you wanted to make them shorter. > Or, perhaps use: rk_drm_*() OK >> +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 pi= pe); >> +void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev); >> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)= ; >> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pip= e); >> +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); > A blank line here helps. OK >> +#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..482f7b8 >> --- /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 Publi= c >> + * License version 2, as published by the Free Software Foundation, a= nd >> + * may be copied, distributed, and modified under those terms. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include "rockchip_drm_drv.h" >> +#include "rockchip_drm_gem.h" >> + >> +#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb) >> + >> +struct rockchip_drm_fb { >> + struct drm_framebuffer fb; >> + struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER]; >> +}; >> + >> +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer= *fb, >> + unsigned int plane) >> +{ >> + struct rockchip_drm_fb *rk_fb =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 *mo= de_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 *file= _priv, >> + struct drm_mode_fb_cmd2 *mode_cmd) >> +{ >> + struct rockchip_drm_fb *rockchip_fb; >> + struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER]; >> + struct drm_gem_object *obj; >> + unsigned int hsub; >> + unsigned int vsub; >> + int num_planes; >> + int ret; >> + int i; >> + >> + hsub =3D drm_format_horz_chroma_subsampling(mode_cmd->pixel_fo= rmat); >> + vsub =3D drm_format_vert_chroma_subsampling(mode_cmd->pixel_fo= rmat); >> + num_planes =3D min(drm_format_num_planes(mode_cmd->pixel_forma= t), >> + 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_f= ormat, 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_fu= ncs =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 limitati= on >> + * 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 Publi= c >> + * License version 2, as published by the Free Software Foundation, a= nd >> + * may be copied, distributed, and modified under those terms. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#ifndef _ROCKCHIP_DRM_FB_H >> +#define _ROCKCHIP_DRM_FB_H >> + >> +struct drm_framebuffer * >> +rockchip_drm_framebuffer_init(struct drm_device *dev, >> + struct drm_mode_fb_cmd2 *mode_cmd, >> + struct drm_gem_object *obj); >> +void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb); >> + >> +void rockchip_drm_mode_config_init(struct drm_device *dev); >> + >> +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer= *fb, >> + unsigned int plane); >> +#endif /* _ROCKCHIP_DRM_FB_H */ >> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/g= pu/drm/rockchip/rockchip_drm_fbdev.c >> new file mode 100644 >> index 0000000..ae563f5 >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c >> @@ -0,0 +1,230 @@ >> +/* >> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >> + * Author:Mark Yao >> + * >> + * This software is licensed under the terms of the GNU General Publi= c >> + * License version 2, as published by the Free Software Foundation, a= nd >> + * 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, h= elper) >> + >> +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->sur= face_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, f= bdev->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_connec= tor) >> + 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= _CONNECTOR); >> + 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 configurat= ion.\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/g= pu/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 Publi= c >> + * License version 2, as published by the Free Software Foundation, a= nd >> + * may be copied, distributed, and modified under those terms. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#ifndef _ROCKCHIP_DRM_FBDEV_H >> +#define _ROCKCHIP_DRM_FBDEV_H >> + >> +int rockchip_drm_fbdev_init(struct drm_device *dev); >> + >> +#endif /* _ROCKCHIP_DRM_FBDEV_H */ >> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu= /drm/rockchip/rockchip_drm_gem.c >> new file mode 100644 >> index 0000000..8e7a91c >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c >> @@ -0,0 +1,341 @@ >> +/* >> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >> + * Author:Mark Yao >> + * >> + * This software is licensed under the terms of the GNU General Publi= c >> + * License version 2, as published by the Free Software Foundation, a= nd >> + * 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 fb= dev */ >> + 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->dm= a_addr, >> + &rk_obj->dma_attrs); >> +} >> + >> +/* drm driver mmap file operations */ >> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *v= ma) >> +{ >> + 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 in= t size) >> +{ >> + 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 ca= llback >> + * 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_stru= ct *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->d= ma_addr, >> + obj->size, &rk_obj->dma_attrs); >> +} >> + >> +/* >> + * rockchip_gem_create_with_handle - allocate an object with the give= n >> + * size and create a gem handle on it >> + * >> + * returns a struct rockchip_gem_object* on success or ERR_PTR values >> + * on failure. >> + */ >> +static struct rockchip_gem_object * >> +rockchip_gem_create_with_handle(struct drm_file *file_priv, >> + struct drm_device *drm, unsigned int s= ize, >> + 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 hand= le, >> + 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 callba= ck >> + * 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); >> + >> + 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, arg= s->size, >> + &args->handle); >> + >> + return PTR_ERR_OR_ZERO(rk_obj); >> +} >> + >> +/* >> + * Allocate a sg_table for this GEM object. >> + * Note: Both the table's contents, and the sg_table itself must be f= reed 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_objec= t *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; >> +} >> + >> +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 *vadd= r) >> +{ >> + /* 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/gpu= /drm/rockchip/rockchip_drm_gem.h >> new file mode 100644 >> index 0000000..e37b921 >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h >> @@ -0,0 +1,55 @@ >> +/* >> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >> + * Author:Mark Yao >> + * >> + * This software is licensed under the terms of the GNU General Publi= c >> + * License version 2, as published by the Free Software Foundation, a= nd >> + * may be copied, distributed, and modified under those terms. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#ifndef _ROCKCHIP_DRM_GEM_H >> +#define _ROCKCHIP_DRM_GEM_H >> + >> +#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object= , base) >> + >> +struct rockchip_gem_object { >> + struct drm_gem_object base; >> + unsigned int flags; >> + >> + void *kvaddr; >> + dma_addr_t dma_addr; >> + struct dma_attrs dma_attrs; >> +}; >> + >> +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_objec= t *obj); >> +struct drm_gem_object * >> +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t siz= e, >> + 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 *vadd= r); >> +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 *v= ma); >> + >> +/* mmap a gem object to userspace. */ >> +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_stru= ct *vma); >> + >> +struct rockchip_gem_object * >> + rockchip_gem_create_object(struct drm_device *drm, unsigned in= t size); >> + >> +void rockchip_gem_free_object(struct drm_gem_object *obj); >> + >> +int rockchip_gem_dumb_create(struct drm_file *file_priv, >> + struct drm_device *dev, >> + struct drm_mode_create_dumb *args); >> +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv, >> + struct drm_device *dev, uint32_t hand= le, >> + uint64_t *offset); >> +#endif /* _ROCKCHIP_DRM_GEM_H */ >> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu= /drm/rockchip/rockchip_drm_vop.c >> new file mode 100644 >> index 0000000..24c3946 >> --- /dev/null >> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c >> @@ -0,0 +1,1373 @@ >> +/* >> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd >> + * Author:Mark Yao >> + * >> + * This software is licensed under the terms of the GNU General Publi= c >> + * License version 2, as published by the Free Software Foundation, a= nd >> + * 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