* [PATCH v9 2/3] dt-bindings: video: Add for rockchip display subsytem
From: Mark Yao @ 2014-09-30 13:04 UTC (permalink / raw)
To: heiko-4mtYJXux2i+zQB+pC5nmwQ, 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,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
xxm-TNX95d0MmH7DzftRWevZcw, huangtao-TNX95d0MmH7DzftRWevZcw,
kever.yang-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, Mark yao
In-Reply-To: <1412081870-27535-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
From: Mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
This add a display subsystem comprise the all display interface nodes.
Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
Changes in v2:
- add DRM master device node to list all display nodes that comprise
the graphics subsystem.
Changes in v3: None
Changes in v4: None
Changes in v5: None
Changes in v6: None
Changes in v7: None
Changes in v8: None
Changes in v9: None
.../devicetree/bindings/video/rockchip-drm.txt | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/rockchip-drm.txt
diff --git a/Documentation/devicetree/bindings/video/rockchip-drm.txt b/Documentation/devicetree/bindings/video/rockchip-drm.txt
new file mode 100644
index 0000000..7fff582
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-drm.txt
@@ -0,0 +1,19 @@
+Rockchip DRM master device
+================================
+
+The Rockchip DRM master device is a virtual device needed to list all
+vop devices or other display interface nodes that comprise the
+graphics subsystem.
+
+Required properties:
+- compatible: Should be "rockchip,display-subsystem"
+- ports: Should contain a list of phandles pointing to display interface port
+ of vop devices. vop definitions as defined in
+ Documentation/devicetree/bindings/video/rockchip-vop.txt
+
+example:
+
+display-subsystem {
+ compatible = "rockchip,display-subsystem";
+ ports = <&vopl_out>, <&vopb_out>;
+};
--
1.7.9.5
^ permalink raw reply related
* [PATCH v9 1/3] drm: rockchip: Add basic drm driver
From: Mark Yao @ 2014-09-30 13:03 UTC (permalink / raw)
To: heiko, 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, linux-doc, linux-kernel, dri-devel, linux-api,
linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq, kfx,
cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw, Mark yao
In-Reply-To: <1412081870-27535-1-git-send-email-mark.yao@rock-chips.com>
From: Mark yao <mark.yao@rock-chips.com>
This patch adds the basic structure of a DRM Driver for Rockchip Socs.
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Daniel Vetter <daniel@ffwll.ch>
Reviewed-by: Rob Clark <robdclark@gmail.com>
---
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 style 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, instead 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 ling 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.
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 | 65 ++
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 | 209 ++++
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 | 1423 +++++++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 196 ++++
14 files changed, 3026 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)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
+obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
obj-$(CONFIG_DRM_GMA500) += gma500/
obj-$(CONFIG_DRM_UDL) += udl/
obj-$(CONFIG_DRM_AST) += ast/
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
new file mode 100644
index 0000000..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/rockchip/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 := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
+ rockchip_drm_gem.o rockchip_drm_vop.o
+
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
new file mode 100644
index 0000000..879b2e0
--- /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 <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/dma-iommu.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_graph.h>
+#include <linux/component.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+
+#define DRIVER_NAME "rockchip"
+#define DRIVER_DESC "RockChip Soc DRM"
+#define DRIVER_DATE "20140818"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+/*
+ * Attach a (component) device to the shared drm dma mapping from master drm
+ * device. This is used by the VOPs to map GEM buffers to a common DMA
+ * mapping.
+ */
+int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
+ struct device *dev)
+{
+ struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
+ int ret;
+
+ ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ dma_set_max_seg_size(dev, 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 long flags)
+{
+ struct rockchip_drm_private *private;
+ struct dma_iommu_mapping *mapping;
+ struct device *dev = drm_dev->dev;
+ int ret;
+
+ private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
+ if (!private)
+ return -ENOMEM;
+
+ drm_dev->dev_private = private;
+
+ drm_mode_config_init(drm_dev);
+
+ rockchip_drm_mode_config_init(drm_dev);
+
+ dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
+ GFP_KERNEL);
+ if (!dev->dma_parms) {
+ ret = -ENOMEM;
+ goto err_config_cleanup;
+ }
+
+ /* TODO(djkurtz): fetch the mapping start/size from somewhere */
+ mapping = arm_iommu_create_mapping(&platform_bus_type, 0x00000000,
+ SZ_2G);
+ if (IS_ERR(mapping)) {
+ ret = PTR_ERR(mapping);
+ goto err_config_cleanup;
+ }
+
+ ret = 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 = arm_iommu_attach_device(dev, mapping);
+ if (ret)
+ goto err_release_mapping;
+
+ /* Try to bind all sub drivers. */
+ ret = component_bind_all(dev, drm_dev);
+ if (ret)
+ goto err_detach_device;
+
+ /* init kms poll for handling hpd */
+ drm_kms_helper_poll_init(drm_dev);
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = true, we can use the vblank feature.
+ */
+ drm_dev->irq_enabled = true;
+
+ /*
+ * with vblank_disable_allowed = true, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ drm_dev->vblank_disable_allowed = true;
+
+ ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
+ if (ret)
+ goto err_kms_helper_poll_fini;
+
+ rockchip_drm_fbdev_init(drm_dev);
+
+ /* force connectors detection */
+ drm_helper_hpd_irq_event(drm_dev);
+
+ return 0;
+
+err_kms_helper_poll_fini:
+ drm_kms_helper_poll_fini(drm_dev);
+ component_unbind_all(dev, drm_dev);
+err_detach_device:
+ arm_iommu_detach_device(dev);
+err_release_mapping:
+ arm_iommu_release_mapping(dev->archdata.mapping);
+err_config_cleanup:
+ drm_mode_config_cleanup(drm_dev);
+ drm_dev->dev_private = NULL;
+ return ret;
+}
+
+static int rockchip_drm_unload(struct drm_device *drm_dev)
+{
+ struct device *dev = drm_dev->dev;
+
+ drm_kms_helper_poll_fini(drm_dev);
+ component_unbind_all(dev, drm_dev);
+ arm_iommu_detach_device(dev);
+ arm_iommu_release_mapping(dev->archdata.mapping);
+ drm_mode_config_cleanup(drm_dev);
+ drm_dev->dev_private = NULL;
+
+ return 0;
+}
+
+void rockchip_drm_lastclose(struct drm_device *dev)
+{
+ struct rockchip_drm_private *priv = dev->dev_private;
+
+ drm_fb_helper_restore_fbdev_mode_unlocked(&priv->fbdev_helper);
+}
+
+static const struct file_operations rockchip_drm_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = rockchip_gem_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .release = drm_release,
+};
+
+const struct vm_operations_struct rockchip_drm_vm_ops = {
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static struct drm_driver rockchip_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+ .load = rockchip_drm_load,
+ .unload = rockchip_drm_unload,
+ .lastclose = rockchip_drm_lastclose,
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = rockchip_drm_crtc_enable_vblank,
+ .disable_vblank = rockchip_drm_crtc_disable_vblank,
+ .gem_vm_ops = &rockchip_drm_vm_ops,
+ .gem_free_object = rockchip_gem_free_object,
+ .dumb_create = rockchip_gem_dumb_create,
+ .dumb_map_offset = rockchip_gem_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
+ .gem_prime_vmap = rockchip_gem_prime_vmap,
+ .gem_prime_vunmap = rockchip_gem_prime_vunmap,
+ .fops = &rockchip_drm_driver_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
+{
+ struct drm_connector *connector;
+
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ int old_dpms = connector->dpms;
+
+ if (connector->funcs->dpms)
+ connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
+ /* Set the old mode back to the connector for resume */
+ connector->dpms = old_dpms;
+ }
+ drm_modeset_unlock_all(dev);
+
+ return 0;
+}
+
+static int rockchip_drm_resume(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->funcs->dpms)
+ connector->funcs->dpms(connector, connector->dpms);
+ }
+ drm_modeset_unlock_all(dev);
+
+ drm_helper_resume_force_mode(dev);
+
+ return 0;
+}
+
+static int rockchip_drm_sys_suspend(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+ pm_message_t message;
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ message.event = PM_EVENT_SUSPEND;
+
+ return rockchip_drm_suspend(drm_dev, message);
+}
+
+static int rockchip_drm_sys_resume(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+ if (!pm_runtime_suspended(dev))
+ return 0;
+
+ return rockchip_drm_resume(drm_dev);
+}
+#endif
+
+static const struct dev_pm_ops rockchip_drm_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
+ rockchip_drm_sys_resume)
+};
+
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
+ struct device_node *np)
+{
+ struct rockchip_drm_private *priv = drm->dev_private;
+ struct device_node *port;
+ int pipe;
+
+ if (priv->num_pipe >= ROCKCHIP_MAX_CRTC)
+ return -EINVAL;
+
+ port = of_get_child_by_name(np, "port");
+ if (!port) {
+ dev_err(drm->dev, "no port node found in %s\n",
+ np->full_name);
+ return -ENXIO;
+ }
+ pipe = priv->num_pipe++;
+ crtc->port = port;
+
+ priv->crtc[pipe] = crtc;
+
+ return pipe;
+}
+
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe)
+{
+ struct rockchip_drm_private *priv = drm->dev_private;
+
+ priv->num_pipe--;
+ of_node_put(priv->crtc[pipe]->port);
+ priv->crtc[pipe] = NULL;
+}
+
+struct drm_crtc *rockchip_drm_find_crtc(struct drm_device *drm, int pipe)
+{
+ struct rockchip_drm_private *priv = drm->dev_private;
+
+ if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe])
+ return priv->crtc[pipe];
+
+ return NULL;
+}
+
+/*
+ * @node: device tree node containing encoder input ports
+ * @encoder: drm_encoder
+ */
+int rockchip_drm_encoder_get_mux_id(struct device_node *node,
+ struct drm_encoder *encoder)
+{
+ struct device_node *ep = NULL;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct of_endpoint endpoint;
+ struct device_node *port;
+ int ret;
+
+ if (!node || !crtc)
+ return -EINVAL;
+
+ do {
+ ep = of_graph_get_next_endpoint(node, ep);
+ if (!ep)
+ break;
+
+ port = of_graph_get_remote_port(ep);
+ of_node_put(port);
+ if (port == crtc->port) {
+ ret = of_graph_parse_endpoint(ep, &endpoint);
+ return ret ?: endpoint.id;
+ }
+ } while (ep);
+
+ return -EINVAL;
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+ struct device_node *np = data;
+
+ return dev->of_node == np;
+}
+
+static void rockchip_add_endpoints(struct device *dev,
+ struct component_match **match,
+ struct device_node *port)
+{
+ struct device_node *ep, *remote;
+
+ for_each_child_of_node(port, ep) {
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote || !of_device_is_available(remote)) {
+ of_node_put(remote);
+ continue;
+ } else if (!of_device_is_available(remote->parent)) {
+ dev_warn(dev, "parent device of %s is not available\n",
+ remote->full_name);
+ of_node_put(remote);
+ continue;
+ }
+
+ component_match_add(dev, match, compare_of, remote);
+ of_node_put(remote);
+ }
+}
+
+static int rockchip_drm_bind(struct device *dev)
+{
+ struct drm_device *drm;
+ int ret;
+
+ drm = drm_dev_alloc(&rockchip_drm_driver, dev);
+ if (!drm)
+ return -ENOMEM;
+
+ ret = drm_dev_set_unique(drm, "%s", dev_name(dev));
+ if (ret)
+ goto err_free;
+
+ ret = drm_dev_register(drm, 0);
+ if (ret)
+ goto err_free;
+
+ dev_set_drvdata(dev, drm);
+
+ return 0;
+
+err_free:
+ drm_dev_unref(drm);
+ return ret;
+}
+
+static void rockchip_drm_unbind(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+
+ drm_dev_unregister(drm);
+ drm_dev_unref(drm);
+}
+
+static const struct component_master_ops rockchip_drm_ops = {
+ .bind = rockchip_drm_bind,
+ .unbind = rockchip_drm_unbind,
+};
+
+static int rockchip_drm_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct component_match *match = NULL;
+ struct device_node *np = dev->of_node;
+ struct device_node *port;
+ int i;
+
+ if (!np)
+ return -ENODEV;
+ /*
+ * Bind the crtc ports first, so that
+ * drm_of_find_possible_crtcs called from encoder .bind callbacks
+ * works as expected.
+ */
+ for (i = 0;; i++) {
+ port = of_parse_phandle(np, "ports", i);
+ if (!port)
+ break;
+
+ 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 == 0) {
+ dev_err(dev, "missing 'ports' property\n");
+ return -ENODEV;
+ }
+ /*
+ * For each bound crtc, bind the encoders attached to its
+ * remote endpoint.
+ */
+ for (i = 0;; i++) {
+ port = of_parse_phandle(np, "ports", i);
+ if (!port)
+ break;
+
+ 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, match);
+}
+
+static int rockchip_drm_platform_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &rockchip_drm_ops);
+
+ return 0;
+}
+
+static const struct of_device_id rockchip_drm_dt_ids[] = {
+ { .compatible = "rockchip,display-subsystem", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
+
+static struct platform_driver rockchip_drm_platform_driver = {
+ .probe = rockchip_drm_platform_probe,
+ .remove = rockchip_drm_platform_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rockchip-drm",
+ .of_match_table = rockchip_drm_dt_ids,
+ .pm = &rockchip_drm_pm_ops,
+ },
+};
+
+module_platform_driver(rockchip_drm_platform_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
+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..1ed9aae
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_DRV_H
+#define _ROCKCHIP_DRM_DRV_H
+
+#include <drm/drm_fb_helper.h>
+
+#include <linux/module.h>
+#include <linux/component.h>
+
+#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_crtc.
+ * @num_pipe: number of pipes for this device.
+ */
+struct rockchip_drm_private {
+ struct drm_fb_helper fbdev_helper;
+ struct drm_gem_object *fbdev_bo;
+ /*
+ * created crtc object would be contained at this array and
+ * this array is used to be aware of which crtc did it request vblank.
+ */
+ struct drm_crtc *crtc[ROCKCHIP_MAX_CRTC];
+
+ unsigned int num_pipe;
+};
+
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
+ struct device_node *port);
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe);
+struct drm_crtc *rockchip_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 connector_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 pipe);
+int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
+ struct device *dev);
+void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
+ struct device *dev);
+
+#endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
new file mode 100644
index 0000000..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 <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
+
+struct rockchip_drm_fb {
+ struct drm_framebuffer fb;
+ struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
+};
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+ unsigned int plane)
+{
+ struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
+
+ if (plane >= ROCKCHIP_MAX_FB_BUFFER)
+ return NULL;
+
+ return rk_fb->obj[plane];
+}
+
+static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+ struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+ struct drm_gem_object *obj;
+ int i;
+
+ for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
+ obj = rockchip_fb->obj[i];
+ if (obj)
+ drm_gem_object_unreference_unlocked(obj);
+ }
+
+ drm_framebuffer_cleanup(fb);
+ kfree(rockchip_fb);
+}
+
+static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle)
+{
+ struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+
+ return drm_gem_handle_create(file_priv,
+ rockchip_fb->obj[0], handle);
+}
+
+static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
+ .destroy = rockchip_drm_fb_destroy,
+ .create_handle = rockchip_drm_fb_create_handle,
+};
+
+static struct rockchip_drm_fb *
+rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object **obj, unsigned int num_planes)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+ int ret;
+ int i;
+
+ rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
+ if (!rockchip_fb)
+ return ERR_PTR(-ENOMEM);
+
+ drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
+
+ for (i = 0; i < num_planes; i++)
+ rockchip_fb->obj[i] = obj[i];
+
+ ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
+ &rockchip_drm_fb_funcs);
+ if (ret) {
+ dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
+ ret);
+ kfree(rockchip_fb);
+ return ERR_PTR(ret);
+ }
+
+ return rockchip_fb;
+}
+
+static struct drm_framebuffer *
+rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+ struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+ struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
+ struct drm_gem_object *obj;
+ unsigned int hsub;
+ unsigned int vsub;
+ int num_planes;
+ int ret;
+ int i;
+
+ hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
+ vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
+ num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
+ ROCKCHIP_MAX_FB_BUFFER);
+
+ for (i = 0; i < num_planes; i++) {
+ unsigned int width = mode_cmd->width / (i ? hsub : 1);
+ unsigned int height = mode_cmd->height / (i ? vsub : 1);
+ unsigned int min_size;
+
+ obj = drm_gem_object_lookup(dev, file_priv,
+ mode_cmd->handles[i]);
+ if (!obj) {
+ dev_err(dev->dev, "Failed to lookup GEM object\n");
+ ret = -ENXIO;
+ goto err_gem_object_unreference;
+ }
+
+ min_size = (height - 1) * mode_cmd->pitches[i] +
+ mode_cmd->offsets[i] +
+ width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
+
+ if (obj->size < min_size) {
+ drm_gem_object_unreference_unlocked(obj);
+ ret = -EINVAL;
+ goto err_gem_object_unreference;
+ }
+ objs[i] = obj;
+ }
+
+ rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
+ if (IS_ERR(rockchip_fb)) {
+ ret = PTR_ERR(rockchip_fb);
+ goto err_gem_object_unreference;
+ }
+
+ return &rockchip_fb->fb;
+
+err_gem_object_unreference:
+ for (i--; i >= 0; i--)
+ drm_gem_object_unreference_unlocked(objs[i]);
+ return ERR_PTR(ret);
+}
+
+static void rockchip_drm_output_poll_changed(struct drm_device *dev)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct drm_fb_helper *fb_helper = &private->fbdev_helper;
+
+ drm_fb_helper_hotplug_event(fb_helper);
+}
+
+static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
+ .fb_create = rockchip_user_fb_create,
+ .output_poll_changed = rockchip_drm_output_poll_changed,
+};
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+
+ rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
+ if (IS_ERR(rockchip_fb))
+ return NULL;
+
+ return &rockchip_fb->fb;
+}
+
+void rockchip_drm_mode_config_init(struct drm_device *dev)
+{
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ /*
+ * set max width and height as default value(4096x4096).
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+
+ dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
new file mode 100644
index 0000000..09574d4
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_FB_H
+#define _ROCKCHIP_DRM_FB_H
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj);
+void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
+
+void rockchip_drm_mode_config_init(struct drm_device *dev);
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+ unsigned int plane);
+#endif /* _ROCKCHIP_DRM_FB_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
new file mode 100644
index 0000000..72fa02c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#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 = info->par;
+ struct rockchip_drm_private *private = to_drm_private(helper);
+
+ return rockchip_gem_mmap_buf(private->fbdev_bo, vma);
+}
+
+static struct fb_ops rockchip_drm_fbdev_ops = {
+ .owner = THIS_MODULE,
+ .fb_mmap = rockchip_fbdev_mmap,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct rockchip_drm_private *private = to_drm_private(helper);
+ struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+ struct drm_device *dev = helper->dev;
+ struct rockchip_gem_object *rk_obj;
+ struct drm_framebuffer *fb;
+ unsigned int bytes_per_pixel;
+ unsigned long offset;
+ struct fb_info *fbi;
+ size_t size;
+ int ret;
+
+ bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+
+ size = mode_cmd.pitches[0] * mode_cmd.height;
+
+ rk_obj = rockchip_gem_create_object(dev, size);
+ if (IS_ERR(rk_obj))
+ return -ENOMEM;
+
+ private->fbdev_bo = &rk_obj->base;
+
+ fbi = framebuffer_alloc(0, dev->dev);
+ if (!fbi) {
+ dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
+ ret = -ENOMEM;
+ goto err_rockchip_gem_free_object;
+ }
+
+ helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd,
+ private->fbdev_bo);
+ if (IS_ERR(helper->fb)) {
+ dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
+ ret = PTR_ERR(helper->fb);
+ goto err_framebuffer_release;
+ }
+
+ helper->fbdev = fbi;
+
+ fbi->par = helper;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->fbops = &rockchip_drm_fbdev_ops;
+
+ ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+ if (ret) {
+ dev_err(dev->dev, "Failed to allocate color map.\n");
+ goto err_drm_framebuffer_unref;
+ }
+
+ fb = helper->fb;
+ drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+ drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
+
+ offset = fbi->var.xoffset * bytes_per_pixel;
+ offset += fbi->var.yoffset * fb->pitches[0];
+
+ dev->mode_config.fb_base = 0;
+ fbi->screen_base = rk_obj->kvaddr + offset;
+ fbi->screen_size = rk_obj->base.size;
+ fbi->fix.smem_len = rk_obj->base.size;
+
+ DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
+ fb->width, fb->height, fb->depth, rk_obj->kvaddr,
+ offset, size);
+ return 0;
+
+err_drm_framebuffer_unref:
+ drm_framebuffer_unreference(helper->fb);
+err_framebuffer_release:
+ framebuffer_release(fbi);
+err_rockchip_gem_free_object:
+ rockchip_gem_free_object(&rk_obj->base);
+ return ret;
+}
+
+static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
+ .fb_probe = rockchip_drm_fbdev_create,
+};
+
+int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct 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 = dev->mode_config.num_crtc;
+
+ helper = &private->fbdev_helper;
+ helper->funcs = &rockchip_drm_fb_helper_funcs;
+
+ ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n",
+ ret);
+ return ret;
+ }
+
+ ret = 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 = 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 = dev->dev_private;
+ struct drm_fb_helper *helper;
+
+ helper = &private->fbdev_helper;
+
+ if (helper->fbdev) {
+ struct fb_info *info;
+ int ret;
+
+ info = helper->fbdev;
+ ret = unregister_framebuffer(info);
+ if (ret < 0)
+ DRM_DEBUG_KMS("failed unregister_framebuffer() - %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 <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_FBDEV_H
+#define _ROCKCHIP_DRM_FBDEV_H
+
+int rockchip_drm_fbdev_init(struct drm_device *dev);
+
+#endif /* _ROCKCHIP_DRM_FBDEV_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
new file mode 100644
index 0000000..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 <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
+
+#include <linux/anon_inodes.h>
+#include <linux/dma-attrs.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj)
+{
+ struct drm_gem_object *obj = &rk_obj->base;
+ struct drm_device *drm = obj->dev;
+
+ init_dma_attrs(&rk_obj->dma_attrs);
+ dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
+
+ /* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
+ rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
+ &rk_obj->dma_addr, GFP_KERNEL,
+ &rk_obj->dma_attrs);
+ if (IS_ERR(rk_obj->kvaddr)) {
+ int ret = PTR_ERR(rk_obj->kvaddr);
+
+ DRM_ERROR("failed to allocate %#x byte dma buffer, %d",
+ obj->size, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
+{
+ struct drm_gem_object *obj = &rk_obj->base;
+ struct drm_device *drm = obj->dev;
+
+ dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
+ &rk_obj->dma_attrs);
+}
+
+int rockchip_gem_mmap_buf(struct drm_gem_object *obj,
+ struct vm_area_struct *vma)
+{
+ struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+ struct drm_device *drm = obj->dev;
+ unsigned long vm_size;
+
+ vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+ vm_size = vma->vm_end - vma->vm_start;
+
+ if (vm_size > obj->size)
+ return -EINVAL;
+
+ return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
+ obj->size, &rk_obj->dma_attrs);
+}
+
+/* drm driver mmap file operations */
+int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_gem_object *obj;
+ struct drm_vma_offset_node *node;
+ int ret;
+
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
+ mutex_lock(&dev->struct_mutex);
+
+ node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
+ vma->vm_pgoff,
+ vma_pages(vma));
+ if (!node) {
+ mutex_unlock(&dev->struct_mutex);
+ DRM_ERROR("failed to find vma node.\n");
+ return -EINVAL;
+ } else if (!drm_vma_node_is_allowed(node, filp)) {
+ mutex_unlock(&dev->struct_mutex);
+ return -EACCES;
+ }
+
+ obj = container_of(node, struct drm_gem_object, vma_node);
+ ret = rockchip_gem_mmap_buf(obj, vma);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
+
+struct rockchip_gem_object *
+ rockchip_gem_create_object(struct drm_device *drm, unsigned int size)
+{
+ struct rockchip_gem_object *rk_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ size = round_up(size, PAGE_SIZE);
+
+ rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
+ if (!rk_obj)
+ return ERR_PTR(-ENOMEM);
+
+ obj = &rk_obj->base;
+
+ drm_gem_private_object_init(drm, obj, size);
+
+ ret = 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 callback
+ * function
+ */
+void rockchip_gem_free_object(struct drm_gem_object *obj)
+{
+ struct rockchip_gem_object *rk_obj;
+
+ drm_gem_free_mmap_offset(obj);
+
+ rk_obj = to_rockchip_obj(obj);
+
+ rockchip_gem_free_buf(rk_obj);
+
+ kfree(rk_obj);
+}
+
+/*
+ * rockchip_gem_create_with_handle - allocate an object with the given
+ * size and create a gem handle on it
+ *
+ * returns a struct rockchip_gem_object* on success or ERR_PTR values
+ * on failure.
+ */
+static struct rockchip_gem_object *
+rockchip_gem_create_with_handle(struct drm_file *file_priv,
+ struct drm_device *drm, unsigned int size,
+ unsigned int *handle)
+{
+ struct rockchip_gem_object *rk_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ rk_obj = rockchip_gem_create_object(drm, size);
+ if (IS_ERR(rk_obj))
+ return ERR_CAST(rk_obj);
+
+ obj = &rk_obj->base;
+
+ /*
+ * allocate a id of idr table where the obj is registered
+ * and handle has the id what user can see.
+ */
+ ret = drm_gem_handle_create(file_priv, obj, handle);
+ if (ret)
+ goto err_handle_create;
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_unreference_unlocked(obj);
+
+ return rk_obj;
+
+err_handle_create:
+ rockchip_gem_free_object(obj);
+
+ return ERR_PTR(ret);
+}
+
+int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset)
+{
+ struct drm_gem_object *obj;
+ int ret;
+
+ mutex_lock(&dev->struct_mutex);
+
+ obj = drm_gem_object_lookup(dev, file_priv, handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret)
+ goto out;
+
+ *offset = drm_vma_node_offset_addr(&obj->vma_node);
+ DRM_DEBUG_KMS("offset = 0x%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 callback
+ * function
+ *
+ * This aligns the pitch and size arguments to the minimum required. wrap
+ * this into your own function if you need bigger alignment.
+ */
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct rockchip_gem_object *rk_obj;
+ int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+ /*
+ * align to 64 bytes since Mali requires it.
+ */
+ min_pitch = ALIGN(min_pitch, 64);
+
+ if (args->pitch < min_pitch)
+ args->pitch = min_pitch;
+
+ if (args->size < args->pitch * args->height)
+ args->size = args->pitch * args->height;
+
+ rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
+ &args->handle);
+
+ return PTR_ERR_OR_ZERO(rk_obj);
+}
+
+/*
+ * Allocate a sg_table for this GEM object.
+ * Note: Both the table's contents, and the sg_table itself must be freed by
+ * the caller.
+ * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
+ */
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+ struct drm_device *drm = obj->dev;
+ struct sg_table *sgt;
+ int ret;
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return ERR_PTR(-ENOMEM);
+
+ ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
+ rk_obj->dma_addr, obj->size,
+ &rk_obj->dma_attrs);
+ if (ret) {
+ DRM_ERROR("failed to allocate sgt, %d\n", ret);
+ kfree(sgt);
+ return ERR_PTR(ret);
+ }
+
+ return sgt;
+}
+
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
+{
+ struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+
+ return rk_obj->kvaddr;
+}
+
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+ /* Nothing to do */
+}
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..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 <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_GEM_H
+#define _ROCKCHIP_DRM_GEM_H
+
+#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
+
+struct rockchip_gem_object {
+ struct drm_gem_object base;
+ unsigned int flags;
+
+ void *kvaddr;
+ dma_addr_t dma_addr;
+ struct dma_attrs dma_attrs;
+};
+
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *
+rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
+ struct sg_table *sgt);
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+
+/* 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 size);
+
+void rockchip_gem_free_object(struct drm_gem_object *obj);
+
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset);
+#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..a6e97cb
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -0,0 +1,1423 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_rect.h>
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+
+#include <linux/reset.h>
+#include <linux/iommu.h>
+#include <linux/delay.h>
+
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_vop.h"
+
+#define VOP_DEFAULT_FRAMERATE 60
+#define VOP_MAX_WIN_SUPPORT 5
+#define VOP_DEFAULT_PRIMARY 0
+#define VOP_DEFAULT_CURSOR 1
+#define VOP_REG(off, _mask, s) \
+ {.offset = off, \
+ .mask = _mask, \
+ .shift = s,}
+
+#define __REG_SET_RELAXED(x, off, mask, shift, v) \
+ vop_mask_write_relaxed(x, off, (mask) << shift, (v) << shift)
+#define __REG_SET_NORMAL(x, off, mask, shift, v) \
+ vop_mask_write(x, off, (mask) << shift, (v) << shift)
+
+#define REG_SET(x, base, reg, v, mode) \
+ __REG_SET_##mode(x, base + reg.offset, reg.mask, reg.shift, v)
+
+#define VOP_WIN_SET(x, win, name, v) \
+ REG_SET(x, win->base, win->phy->name, v, RELAXED)
+#define VOP_CTRL_SET(x, name, v) \
+ REG_SET(x, 0, (x)->data->ctrl->name, v, NORMAL)
+
+#define VOP_WIN_GET_YRGBADDR(ctx, win) \
+ vop_readl(ctx, win->base + win->phy->yrgb_mst.offset)
+
+#define to_vop_ctx(x) container_of(x, struct vop_context, crtc)
+#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
+
+struct rockchip_plane {
+ int id;
+ struct drm_plane base;
+ const struct vop_win *win;
+
+ uint32_t pending_yrgb_mst;
+ struct drm_framebuffer *front_fb;
+ struct drm_framebuffer *pending_fb;
+ bool enabled;
+};
+
+struct vop_context {
+ struct device *dev;
+ struct drm_device *drm_dev;
+ struct drm_crtc crtc;
+ struct drm_pending_vblank_event *event;
+ struct vop_driver *drv;
+ unsigned int dpms;
+
+ int connector_type;
+ int connector_out_mode;
+ wait_queue_head_t wait_vsync_queue;
+ atomic_t wait_vsync_event;
+
+ struct workqueue_struct *vsync_wq;
+ struct work_struct vsync_work;
+
+ /* mutex vsync_ work */
+ struct mutex vsync_mutex;
+ bool vsync_work_pending;
+
+ struct vop_driver_data *data;
+
+ uint32_t *regsbak;
+ void __iomem *regs;
+
+ /* physical map length of vop register */
+ uint32_t len;
+
+ /* one time only one process allowed to config the register */
+ spinlock_t reg_lock;
+ /* lock vop irq reg */
+ spinlock_t irq_lock;
+
+ unsigned int irq;
+
+ /* vop AHP clk */
+ struct clk *hclk;
+ /* vop dclk */
+ struct clk *dclk;
+ /* vop share memory frequency */
+ struct clk *aclk;
+
+ /* vop dclk reset */
+ struct reset_control *dclk_rst;
+
+ int pipe;
+ bool clk_on;
+ bool is_iommu_attach;
+};
+
+enum vop_data_format {
+ VOP_FMT_ARGB8888 = 0,
+ VOP_FMT_RGB888,
+ VOP_FMT_RGB565,
+ VOP_FMT_YUV420SP = 4,
+ VOP_FMT_YUV422SP,
+ VOP_FMT_YUV444SP,
+};
+
+struct vop_reg_data {
+ uint32_t offset;
+ uint32_t value;
+};
+
+struct vop_reg {
+ uint32_t offset;
+ uint32_t shift;
+ uint32_t mask;
+};
+
+struct vop_ctrl {
+ struct vop_reg standby;
+ struct vop_reg data_blank;
+ struct vop_reg gate_en;
+ struct vop_reg mmu_en;
+ struct vop_reg rgb_en;
+ struct vop_reg edp_en;
+ struct vop_reg hdmi_en;
+ struct vop_reg mipi_en;
+ struct vop_reg out_mode;
+ struct vop_reg dither_down;
+ struct vop_reg dither_up;
+ struct vop_reg pin_pol;
+
+ struct vop_reg htotal_pw;
+ struct vop_reg hact_st_end;
+ struct vop_reg vtotal_pw;
+ struct vop_reg vact_st_end;
+ struct vop_reg hpost_st_end;
+ struct vop_reg vpost_st_end;
+};
+
+struct vop_win_phy {
+ const uint32_t *data_formats;
+ uint32_t nformats;
+
+ struct vop_reg enable;
+ struct vop_reg format;
+ struct vop_reg act_info;
+ struct vop_reg dsp_info;
+ struct vop_reg dsp_st;
+ struct vop_reg yrgb_mst;
+ struct vop_reg uv_mst;
+ struct vop_reg yrgb_vir;
+ struct vop_reg uv_vir;
+
+ struct vop_reg dst_alpha_ctl;
+ struct vop_reg src_alpha_ctl;
+};
+
+struct vop_win {
+ uint32_t base;
+ const struct vop_win_phy *phy;
+};
+
+struct vop_driver_data {
+ const void *init_table;
+ int table_size;
+ const struct vop_ctrl *ctrl;
+ const struct vop_win *win[VOP_MAX_WIN_SUPPORT];
+};
+
+static const uint32_t formats_01[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_NV12,
+ DRM_FORMAT_NV16,
+ DRM_FORMAT_NV24,
+};
+
+static const uint32_t formats_234[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_RGB565,
+};
+
+static const struct vop_win_phy win01_data = {
+ .data_formats = formats_01,
+ .nformats = ARRAY_SIZE(formats_01),
+ .enable = VOP_REG(WIN0_CTRL0, 0x1, 0),
+ .format = VOP_REG(WIN0_CTRL0, 0x7, 1),
+ .act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0),
+ .dsp_info = VOP_REG(WIN0_DSP_INFO, 0x0fff0fff, 0),
+ .dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(WIN0_YRGB_MST, 0xffffffff, 0),
+ .uv_mst = VOP_REG(WIN0_CBR_MST, 0xffffffff, 0),
+ .yrgb_vir = VOP_REG(WIN0_VIR, 0x3fff, 0),
+ .uv_vir = VOP_REG(WIN0_VIR, 0x3fff, 16),
+ .src_alpha_ctl = VOP_REG(WIN0_SRC_ALPHA_CTRL, 0xff, 0),
+ .dst_alpha_ctl = VOP_REG(WIN0_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_phy win23_data = {
+ .data_formats = formats_234,
+ .nformats = ARRAY_SIZE(formats_234),
+ .enable = VOP_REG(WIN2_CTRL0, 0x1, 0),
+ .format = VOP_REG(WIN2_CTRL0, 0x7, 1),
+ .dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0),
+ .dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0),
+ .yrgb_vir = VOP_REG(WIN2_VIR0_1, 0x1fff, 0),
+ .src_alpha_ctl = VOP_REG(WIN2_SRC_ALPHA_CTRL, 0xff, 0),
+ .dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_phy cursor_data = {
+ .data_formats = formats_234,
+ .nformats = ARRAY_SIZE(formats_234),
+ .enable = VOP_REG(HWC_CTRL0, 0x1, 0),
+ .format = VOP_REG(HWC_CTRL0, 0x7, 1),
+ .dsp_st = VOP_REG(HWC_DSP_ST, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(HWC_MST, 0xffffffff, 0),
+};
+
+static const struct vop_win win0 = {
+ .base = 0,
+ .phy = &win01_data,
+};
+
+static const struct vop_win win1 = {
+ .base = 0x40,
+ .phy = &win01_data,
+};
+
+static const struct vop_win win2 = {
+ .base = 0,
+ .phy = &win23_data,
+};
+
+static const struct vop_win win3 = {
+ .base = 0x50,
+ .phy = &win23_data,
+};
+
+static const struct vop_win win_cursor = {
+ .base = 0,
+ .phy = &cursor_data,
+};
+
+static const struct vop_ctrl ctrl_data = {
+ .standby = VOP_REG(SYS_CTRL, 0x1, 22),
+ .gate_en = VOP_REG(SYS_CTRL, 0x1, 23),
+ .mmu_en = VOP_REG(SYS_CTRL, 0x1, 20),
+ .rgb_en = VOP_REG(SYS_CTRL, 0x1, 12),
+ .hdmi_en = VOP_REG(SYS_CTRL, 0x1, 13),
+ .edp_en = VOP_REG(SYS_CTRL, 0x1, 14),
+ .mipi_en = VOP_REG(SYS_CTRL, 0x1, 15),
+ .dither_down = VOP_REG(DSP_CTRL1, 0xf, 1),
+ .dither_up = VOP_REG(DSP_CTRL1, 0x1, 6),
+ .data_blank = VOP_REG(DSP_CTRL0, 0x1, 19),
+ .out_mode = VOP_REG(DSP_CTRL0, 0xf, 0),
+ .pin_pol = VOP_REG(DSP_CTRL0, 0xf, 4),
+ .htotal_pw = VOP_REG(DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+ .hact_st_end = VOP_REG(DSP_HACT_ST_END, 0x1fff1fff, 0),
+ .vtotal_pw = VOP_REG(DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+ .vact_st_end = VOP_REG(DSP_VACT_ST_END, 0x1fff1fff, 0),
+ .hpost_st_end = VOP_REG(POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+ .vpost_st_end = VOP_REG(POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+};
+
+static const struct vop_reg_data vop_init_reg_table[] = {
+ {SYS_CTRL, 0x00c00000},
+ {DSP_CTRL0, 0x00000000},
+ {WIN0_CTRL0, 0x00000080},
+ {WIN1_CTRL0, 0x00000080},
+};
+
+static const struct vop_driver_data rockchip_rk3288_vop = {
+ .init_table = vop_init_reg_table,
+ .table_size = ARRAY_SIZE(vop_init_reg_table),
+ .ctrl = &ctrl_data,
+ .win[0] = &win0,
+ .win[1] = &win1,
+ .win[2] = &win2,
+ .win[3] = &win3,
+ .win[4] = &win_cursor,
+};
+
+static const struct of_device_id vop_driver_dt_match[] = {
+ { .compatible = "rockchip,rk3288-vop",
+ .data = (void *)&rockchip_rk3288_vop },
+ {},
+};
+
+static inline void vop_writel(struct vop_context *ctx,
+ uint32_t offset, uint32_t v)
+{
+ writel(v, ctx->regs + offset);
+ ctx->regsbak[offset >> 2] = v;
+}
+
+static inline uint32_t vop_readl(struct vop_context *ctx, uint32_t offset)
+{
+ return readl(ctx->regs + offset);
+}
+
+static inline void vop_cfg_done(struct vop_context *ctx)
+{
+ writel(0x01, ctx->regs + REG_CFG_DONE);
+}
+
+static inline void vop_mask_write(struct vop_context *ctx,
+ uint32_t offset, uint32_t mask, uint32_t v)
+{
+ if (mask) {
+ uint32_t cached_val = ctx->regsbak[offset >> 2];
+
+ cached_val = (cached_val & ~mask) | v;
+ writel(cached_val, ctx->regs + offset);
+ ctx->regsbak[offset >> 2] = cached_val;
+ }
+}
+
+static inline void vop_mask_write_relaxed(struct vop_context *ctx,
+ uint32_t offset, uint32_t mask,
+ uint32_t v)
+{
+ if (mask) {
+ uint32_t cached_val = ctx->regsbak[offset >> 2];
+
+ cached_val = (cached_val & ~mask) | v;
+ writel_relaxed(cached_val, ctx->regs + offset);
+ ctx->regsbak[offset >> 2] = cached_val;
+ }
+}
+
+static inline struct vop_driver_data *vop_get_driver_data(struct device *dev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(vop_driver_dt_match, dev);
+
+ return (struct vop_driver_data *)of_id->data;
+}
+
+static enum vop_data_format vop_convert_format(uint32_t format)
+{
+ switch (format) {
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB8888:
+ return VOP_FMT_ARGB8888;
+ case DRM_FORMAT_RGB888:
+ return VOP_FMT_RGB888;
+ case DRM_FORMAT_RGB565:
+ return VOP_FMT_RGB565;
+ case DRM_FORMAT_NV12:
+ return VOP_FMT_YUV420SP;
+ case DRM_FORMAT_NV16:
+ return VOP_FMT_YUV422SP;
+ case DRM_FORMAT_NV24:
+ return VOP_FMT_YUV444SP;
+ default:
+ DRM_ERROR("unsupport format[%08x]\n", format);
+ return -EINVAL;
+ }
+}
+
+static bool is_alpha_support(uint32_t format)
+{
+ switch (format) {
+ case DRM_FORMAT_ARGB8888:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* TODO(djkurtz): move generic 'setup slave rk_iommu' code somewhere common */
+static int vop_iommu_init(struct vop_context *ctx)
+{
+ struct device *dev = ctx->dev;
+ struct device_node *np = dev->of_node;
+ struct platform_device *pd;
+ int count;
+ int ret;
+ struct of_phandle_args args;
+
+ /* Each VOP must have exactly one iommu node, with no args */
+ count = of_count_phandle_with_args(np, "iommus", "#iommu-cells");
+ if (count != 1) {
+ dev_err(dev, "of_count_phandle_with_args(%s) => %d\n",
+ np->full_name, count);
+ return -EINVAL;
+ }
+
+ ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
+ &args);
+ if (ret) {
+ dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n",
+ np->full_name, ret);
+ return ret;
+ }
+ if (args.args_count != 0) {
+ dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n",
+ args.np->full_name, args.args_count);
+ return -EINVAL;
+ }
+
+ pd = of_find_device_by_node(args.np);
+ of_node_put(args.np);
+ if (!pd) {
+ dev_err(dev, "iommu %s not found\n", args.np->full_name);
+ return -EPROBE_DEFER;
+ }
+
+ /* TODO(djkurtz): handle multiple slave iommus for a single master */
+ dev->archdata.iommu = &pd->dev;
+
+ return 0;
+}
+
+static void rockchip_power_on(struct drm_crtc *crtc)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ int ret;
+
+ ret = clk_enable(ctx->hclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to enable hclk - %d\n", ret);
+ return;
+ }
+
+ ret = clk_enable(ctx->dclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to enable dclk - %d\n", ret);
+ goto err_disable_hclk;
+ }
+
+ ret = clk_enable(ctx->aclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to enable aclk - %d\n", ret);
+ goto err_disable_dclk;
+ }
+ if (!ctx->is_iommu_attach) {
+ /*
+ * when we attach iommu device, we sould sure the vop scan at
+ * correct address.
+ */
+ ret = rockchip_drm_dma_attach_device(ctx->drm_dev, ctx->dev);
+ if (ret) {
+ dev_err(ctx->dev, "failed to attach dma mapping, %d\n",
+ ret);
+ goto err_disable_aclk;
+ }
+ ctx->is_iommu_attach = true;
+ }
+
+ spin_lock(&ctx->reg_lock);
+
+ VOP_CTRL_SET(ctx, standby, 0);
+
+ spin_unlock(&ctx->reg_lock);
+
+ return;
+
+err_disable_aclk:
+ clk_disable(ctx->aclk);
+err_disable_dclk:
+ clk_disable(ctx->dclk);
+err_disable_hclk:
+ clk_disable(ctx->hclk);
+ return;
+}
+
+static void rockchip_power_off(struct drm_crtc *crtc)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+
+ drm_vblank_off(crtc->dev, ctx->pipe);
+
+
+ spin_lock(&ctx->reg_lock);
+
+ VOP_CTRL_SET(ctx, standby, 1);
+
+ spin_unlock(&ctx->reg_lock);
+ /*
+ * disable dclk to stop frame scan, so we can safely detach iommu,
+ */
+ clk_disable(ctx->dclk);
+
+ if (ctx->is_iommu_attach) {
+ rockchip_drm_dma_detach_device(ctx->drm_dev, ctx->dev);
+ ctx->is_iommu_attach = false;
+ }
+
+ clk_disable(ctx->aclk);
+ clk_disable(ctx->hclk);
+}
+
+static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x,
+ int crtc_y, unsigned int crtc_w,
+ unsigned int crtc_h, uint32_t src_x,
+ uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ const struct vop_win *win = rockchip_plane->win;
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ struct drm_gem_object *obj;
+ struct rockchip_gem_object *rk_obj;
+ unsigned long offset;
+ unsigned int actual_w;
+ unsigned int actual_h;
+ unsigned int dsp_stx;
+ unsigned int dsp_sty;
+ unsigned int y_vir_stride;
+ dma_addr_t yrgb_mst;
+ enum vop_data_format format;
+ uint32_t val;
+ bool is_alpha;
+ bool visible;
+ int ret;
+ struct drm_rect dest = {
+ .x1 = crtc_x,
+ .y1 = crtc_y,
+ .x2 = crtc_x + crtc_w,
+ .y2 = crtc_y + crtc_h,
+ };
+ struct drm_rect src = {
+ /* 16.16 fixed point */
+ .x1 = src_x,
+ .y1 = src_y,
+ .x2 = src_x + src_w,
+ .y2 = src_y + src_h,
+ };
+ const struct drm_rect clip = {
+ .x2 = crtc->mode.hdisplay,
+ .y2 = crtc->mode.vdisplay,
+ };
+ bool can_position = plane->type != DRM_PLANE_TYPE_PRIMARY;
+
+ ret = drm_plane_helper_check_update(plane, crtc, fb,
+ &src, &dest, &clip,
+ DRM_PLANE_HELPER_NO_SCALING,
+ DRM_PLANE_HELPER_NO_SCALING,
+ can_position, false, &visible);
+ if (ret)
+ return ret;
+
+ if (!visible)
+ return 0;
+
+ is_alpha = is_alpha_support(fb->pixel_format);
+ format = vop_convert_format(fb->pixel_format);
+ if (format < 0)
+ return format;
+
+ obj = rockchip_fb_get_gem_obj(fb, 0);
+ if (!obj) {
+ DRM_ERROR("fail to get rockchip gem object from framebuffer\n");
+ return -EINVAL;
+ }
+
+ rk_obj = to_rockchip_obj(obj);
+
+ yrgb_mst = rk_obj->dma_addr;
+ actual_w = (src.x2 - src.x1) >> 16;
+ actual_h = (src.y2 - src.y1) >> 16;
+ crtc_x = max(0, crtc_x);
+ crtc_y = max(0, crtc_y);
+
+ dsp_stx = crtc_x + crtc->mode.htotal - crtc->mode.hsync_start;
+ dsp_sty = crtc_y + crtc->mode.vtotal - crtc->mode.vsync_start;
+
+ offset = (src.x1 >> 16) * (fb->bits_per_pixel >> 3);
+ offset += (src.y1 >> 16) * fb->pitches[0];
+
+ y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
+
+ spin_lock(&ctx->reg_lock);
+
+ VOP_WIN_SET(ctx, win, format, format);
+ VOP_WIN_SET(ctx, win, yrgb_vir, y_vir_stride);
+ yrgb_mst += offset;
+ VOP_WIN_SET(ctx, win, yrgb_mst, yrgb_mst);
+ val = (actual_h - 1) << 16;
+ val |= (actual_w - 1) & 0xffff;
+ VOP_WIN_SET(ctx, win, act_info, val);
+ VOP_WIN_SET(ctx, win, dsp_info, val);
+ val = (dsp_sty - 1) << 16;
+ val |= (dsp_stx - 1) & 0xffff;
+ VOP_WIN_SET(ctx, win, dsp_st, val);
+
+ if (is_alpha) {
+ VOP_WIN_SET(ctx, win, dst_alpha_ctl,
+ DST_FACTOR_M0(ALPHA_SRC_INVERSE));
+ val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
+ SRC_ALPHA_M0(ALPHA_STRAIGHT) |
+ SRC_BLEND_M0(ALPHA_PER_PIX) |
+ SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
+ SRC_FACTOR_M0(ALPHA_ONE);
+ VOP_WIN_SET(ctx, win, src_alpha_ctl, val);
+ } else {
+ VOP_WIN_SET(ctx, win, src_alpha_ctl, SRC_ALPHA_EN(0));
+ }
+
+ VOP_WIN_SET(ctx, win, enable, 1);
+
+ spin_unlock(&ctx->reg_lock);
+
+ mutex_lock(&ctx->vsync_mutex);
+
+ /*
+ * Because the buffer set to vop take effect at frame start time,
+ * we need make sure old buffer is not in use before we release
+ * it.
+ * reference the framebuffer, and unference it when it swap out of vop.
+ */
+ if (fb != rockchip_plane->front_fb) {
+ drm_framebuffer_reference(fb);
+ if (rockchip_plane->pending_fb)
+ drm_framebuffer_unreference(rockchip_plane->pending_fb);
+ rockchip_plane->pending_fb = fb;
+ rockchip_plane->pending_yrgb_mst = yrgb_mst;
+ ctx->vsync_work_pending = true;
+ }
+ rockchip_plane->enabled = true;
+
+ mutex_unlock(&ctx->vsync_mutex);
+
+ spin_lock(&ctx->reg_lock);
+ vop_cfg_done(ctx);
+ spin_unlock(&ctx->reg_lock);
+
+ return 0;
+}
+
+static inline int rockchip_update_primary_plane(struct drm_crtc *crtc)
+{
+ unsigned int crtc_w, crtc_h;
+
+ crtc_w = crtc->primary->fb->width - crtc->x;
+ crtc_h = crtc->primary->fb->height - crtc->y;
+
+ return rockchip_update_plane(crtc->primary, crtc, crtc->primary->fb,
+ 0, 0, crtc_w, crtc_h, crtc->x << 16,
+ crtc->y << 16, crtc_w << 16, crtc_h << 16);
+}
+
+static int rockchip_disable_plane(struct drm_plane *plane)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ const struct vop_win *win = rockchip_plane->win;
+ struct vop_context *ctx;
+
+ if (!plane->crtc || !rockchip_plane->enabled)
+ return 0;
+
+ ctx = to_vop_ctx(plane->crtc);
+ spin_lock(&ctx->reg_lock);
+
+ VOP_WIN_SET(ctx, win, enable, 0);
+ vop_cfg_done(ctx);
+
+ spin_unlock(&ctx->reg_lock);
+
+ mutex_lock(&ctx->vsync_mutex);
+
+ /*
+ * clear the pending framebuffer and set vsync_work_pending true,
+ * so that the framebuffer will unref at the next vblank.
+ */
+ if (rockchip_plane->pending_fb) {
+ drm_framebuffer_unreference(rockchip_plane->pending_fb);
+ rockchip_plane->pending_fb = NULL;
+ }
+
+ rockchip_plane->enabled = false;
+ ctx->vsync_work_pending = true;
+
+ mutex_unlock(&ctx->vsync_mutex);
+
+ return 0;
+}
+
+static void rockchip_plane_destroy(struct drm_plane *plane)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+
+ rockchip_disable_plane(plane);
+ drm_plane_cleanup(plane);
+ kfree(rockchip_plane);
+}
+
+static const struct drm_plane_funcs rockchip_plane_funcs = {
+ .update_plane = rockchip_update_plane,
+ .disable_plane = rockchip_disable_plane,
+ .destroy = rockchip_plane_destroy,
+};
+
+static struct drm_plane *rockchip_plane_init(struct vop_context *ctx,
+ unsigned long possible_crtcs,
+ enum drm_plane_type type,
+ int index)
+{
+ struct rockchip_plane *rockchip_plane;
+ struct vop_driver_data *vop_data = ctx->data;
+ const struct vop_win *win;
+ int err;
+
+ if (index >= VOP_MAX_WIN_SUPPORT)
+ return ERR_PTR(-EINVAL);
+
+ rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
+ if (!rockchip_plane)
+ return ERR_PTR(-ENOMEM);
+
+ win = vop_data->win[index];
+ rockchip_plane->id = index;
+ rockchip_plane->win = win;
+
+ err = drm_universal_plane_init(ctx->drm_dev, &rockchip_plane->base,
+ possible_crtcs, &rockchip_plane_funcs,
+ win->phy->data_formats,
+ win->phy->nformats, type);
+ if (err) {
+ DRM_ERROR("failed to initialize plane\n");
+ kfree(rockchip_plane);
+ return ERR_PTR(err);
+ }
+
+ return &rockchip_plane->base;
+}
+
+int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc,
+ int connector_type,
+ int out_mode)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+
+ ctx->connector_type = connector_type;
+ ctx->connector_out_mode = out_mode;
+
+ return 0;
+}
+
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
+{
+ struct vop_context *ctx = to_vop_ctx(rockchip_drm_find_crtc(dev, pipe));
+ unsigned long flags;
+
+ if (ctx->dpms != DRM_MODE_DPMS_ON)
+ return -EPERM;
+
+ spin_lock_irqsave(&ctx->irq_lock, flags);
+
+ vop_mask_write(ctx, INTR_CTRL0, FS_INTR_MASK,
+ FS_INTR_EN(1));
+
+ spin_unlock_irqrestore(&ctx->irq_lock, flags);
+
+ return 0;
+}
+
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
+{
+ struct vop_context *ctx = to_vop_ctx(rockchip_drm_find_crtc(dev, pipe));
+ unsigned long flags;
+
+ if (ctx->dpms != DRM_MODE_DPMS_ON)
+ return;
+ spin_lock_irqsave(&ctx->irq_lock, flags);
+ vop_mask_write(ctx, INTR_CTRL0, FS_INTR_MASK,
+ FS_INTR_EN(0));
+ spin_unlock_irqrestore(&ctx->irq_lock, flags);
+}
+
+static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+
+ DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
+
+ if (ctx->dpms == mode) {
+ DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+ return;
+ }
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ rockchip_power_on(crtc);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ rockchip_power_off(crtc);
+ break;
+ default:
+ DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+ break;
+ }
+
+ ctx->dpms = mode;
+}
+
+static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+ rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
+ return false;
+
+ return true;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb);
+
+static int rockchip_drm_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *fb)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
+ u16 hdisplay = adjusted_mode->hdisplay;
+ u16 htotal = adjusted_mode->htotal;
+ u16 hact_st = adjusted_mode->htotal - adjusted_mode->hsync_start;
+ u16 hact_end = hact_st + hdisplay;
+ u16 vdisplay = adjusted_mode->vdisplay;
+ u16 vtotal = adjusted_mode->vtotal;
+ u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
+ u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
+ u16 vact_end = vact_st + vdisplay;
+ int ret;
+ uint32_t val;
+
+ /*
+ * disable dclk to stop frame scan, so that we can safe config mode and
+ * enable iommu.
+ */
+ clk_disable(ctx->dclk);
+
+ ret = rockchip_drm_crtc_mode_set_base(crtc, x, y, fb);
+ if (ret)
+ return ret;
+
+ switch (ctx->connector_type) {
+ case DRM_MODE_CONNECTOR_LVDS:
+ VOP_CTRL_SET(ctx, rgb_en, 1);
+ break;
+ case DRM_MODE_CONNECTOR_eDP:
+ VOP_CTRL_SET(ctx, edp_en, 1);
+ break;
+ case DRM_MODE_CONNECTOR_HDMIA:
+ VOP_CTRL_SET(ctx, hdmi_en, 1);
+ break;
+ default:
+ DRM_ERROR("unsupport connector_type[%d]\n",
+ ctx->connector_type);
+ return -EINVAL;
+ };
+ VOP_CTRL_SET(ctx, out_mode, ctx->connector_out_mode);
+
+ val = 0x8;
+ val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0;
+ val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? (1 << 1) : 0;
+ VOP_CTRL_SET(ctx, pin_pol, val);
+
+ VOP_CTRL_SET(ctx, htotal_pw, (htotal << 16) | hsync_len);
+ val = hact_st << 16;
+ val |= hact_end;
+ VOP_CTRL_SET(ctx, hact_st_end, val);
+ VOP_CTRL_SET(ctx, hpost_st_end, val);
+
+ VOP_CTRL_SET(ctx, vtotal_pw, (vtotal << 16) | vsync_len);
+ val = vact_st << 16;
+ val |= vact_end;
+ VOP_CTRL_SET(ctx, vact_st_end, val);
+ VOP_CTRL_SET(ctx, vpost_st_end, val);
+ /*
+ * reset dclk, take all mode config affect, so the clk would run in
+ * correct frame.
+ */
+ reset_control_assert(ctx->dclk_rst);
+ usleep_range(10, 20);
+ reset_control_deassert(ctx->dclk_rst);
+
+ clk_set_rate(ctx->dclk, adjusted_mode->clock * 1000);
+ ret = clk_enable(ctx->dclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to enable dclk - %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ int ret;
+
+ crtc->x = x;
+ crtc->y = y;
+
+ ret = rockchip_update_primary_plane(crtc);
+ if (ret < 0) {
+ DRM_ERROR("fail to update plane\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
+{
+ /* just do dummy now */
+}
+
+static const struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
+ .dpms = rockchip_drm_crtc_dpms,
+ .prepare = rockchip_drm_crtc_prepare,
+ .mode_fixup = rockchip_drm_crtc_mode_fixup,
+ .mode_set = rockchip_drm_crtc_mode_set,
+ .mode_set_base = rockchip_drm_crtc_mode_set_base,
+ .commit = rockchip_drm_crtc_commit,
+};
+
+static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event,
+ uint32_t page_flip_flags)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ struct drm_framebuffer *old_fb = crtc->primary->fb;
+ int ret;
+
+ /* when the page flip is requested, crtc's dpms should be on */
+ if (ctx->dpms > DRM_MODE_DPMS_ON) {
+ DRM_DEBUG("failed page flip request at dpms[%d].\n", ctx->dpms);
+ return 0;
+ }
+
+ ret = drm_vblank_get(dev, ctx->pipe);
+ if (ret) {
+ DRM_DEBUG("failed to acquire vblank counter\n");
+ return ret;
+ }
+
+ spin_lock_irq(&dev->event_lock);
+ if (ctx->event) {
+ spin_unlock_irq(&dev->event_lock);
+ DRM_ERROR("already pending flip!\n");
+ return -EBUSY;
+ }
+ ctx->event = event;
+ atomic_set(&ctx->wait_vsync_event, 1);
+ spin_unlock_irq(&dev->event_lock);
+
+ crtc->primary->fb = fb;
+
+ ret = rockchip_update_primary_plane(crtc);
+ if (ret) {
+ crtc->primary->fb = old_fb;
+
+ spin_lock_irq(&dev->event_lock);
+ drm_vblank_put(dev, ctx->pipe);
+ atomic_set(&ctx->wait_vsync_event, 0);
+ ctx->event = NULL;
+ spin_unlock_irq(&dev->event_lock);
+ }
+
+ return ret;
+}
+
+static void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
+{
+ struct rockchip_drm_private *dev_priv = dev->dev_private;
+ struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
+ struct vop_context *ctx;
+ unsigned long flags;
+
+ ctx = to_vop_ctx(drm_crtc);
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+
+ if (ctx->event) {
+ drm_send_vblank_event(dev, -1, ctx->event);
+ drm_vblank_put(dev, pipe);
+ atomic_set(&ctx->wait_vsync_event, 0);
+ wake_up(&ctx->wait_vsync_queue);
+ ctx->event = NULL;
+ }
+
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ struct rockchip_drm_private *private = crtc->dev->dev_private;
+
+ private->crtc[ctx->pipe] = NULL;
+ drm_crtc_cleanup(crtc);
+}
+
+static const struct drm_crtc_funcs rockchip_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .page_flip = rockchip_drm_crtc_page_flip,
+ .destroy = rockchip_drm_crtc_destroy,
+};
+
+static void rockchip_vsync_worker(struct work_struct *work)
+{
+ struct vop_context *ctx = container_of(work, struct vop_context,
+ vsync_work);
+ struct drm_device *drm = ctx->drm_dev;
+ struct rockchip_drm_private *dev_priv = drm->dev_private;
+ struct drm_crtc *crtc = dev_priv->crtc[ctx->pipe];
+ struct rockchip_plane *rockchip_plane;
+ struct drm_plane *plane;
+ uint32_t yrgb_mst;
+
+ mutex_lock(&ctx->vsync_mutex);
+
+ ctx->vsync_work_pending = false;
+
+ list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
+ rockchip_plane = to_rockchip_plane(plane);
+
+ if (to_vop_ctx(plane->crtc) != ctx)
+ continue;
+ if (rockchip_plane->enabled && !rockchip_plane->pending_fb)
+ continue;
+ if (!rockchip_plane->enabled && !rockchip_plane->front_fb)
+ continue;
+ /*
+ * make sure the yrgb_mst take effect, so that
+ * we can unreference the old framebuffer.
+ */
+ yrgb_mst = VOP_WIN_GET_YRGBADDR(ctx, rockchip_plane->win);
+ if (rockchip_plane->pending_yrgb_mst != yrgb_mst) {
+ /*
+ * some plane no complete, unref at next vblank
+ */
+ ctx->vsync_work_pending = true;
+ continue;
+ }
+
+ /*
+ * drm_framebuffer_unreference maybe call iommu unmap,
+ * and iommu not allow unmap buffer at irq context,
+ * so we do drm_framebuffer_unreference at queue_work.
+ */
+ if (rockchip_plane->front_fb)
+ drm_framebuffer_unreference(rockchip_plane->front_fb);
+
+ rockchip_plane->front_fb = rockchip_plane->pending_fb;
+ rockchip_plane->pending_fb = NULL;
+
+ /*
+ * if primary plane flip complete, sending the event to
+ * userspace
+ */
+ if (&rockchip_plane->base == crtc->primary)
+ rockchip_drm_crtc_finish_pageflip(ctx->drm_dev,
+ ctx->pipe);
+ }
+
+ mutex_unlock(&ctx->vsync_mutex);
+}
+
+static irqreturn_t rockchip_vop_isr(int irq, void *data)
+{
+ struct vop_context *ctx = data;
+ uint32_t intr0_reg;
+ unsigned long flags;
+
+ intr0_reg = vop_readl(ctx, INTR_CTRL0);
+ if (intr0_reg & FS_INTR) {
+ spin_lock_irqsave(&ctx->irq_lock, flags);
+ vop_writel(ctx, INTR_CTRL0, intr0_reg | FS_INTR_CLR);
+ spin_unlock_irqrestore(&ctx->irq_lock, flags);
+ } else {
+ return IRQ_NONE;
+ }
+
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ if (ctx->vsync_work_pending)
+ queue_work(ctx->vsync_wq, &ctx->vsync_work);
+
+ return IRQ_HANDLED;
+}
+
+static int vop_create_crtc(struct vop_context *ctx)
+{
+ struct device *dev = ctx->dev;
+ struct drm_device *drm_dev = ctx->drm_dev;
+ struct drm_plane *primary, *cursor;
+ unsigned long possible_crtcs;
+ struct drm_crtc *crtc;
+ int ret;
+ int nr;
+
+ crtc = &ctx->crtc;
+
+ ret = rockchip_drm_add_crtc(drm_dev, crtc, dev->of_node);
+ if (ret < 0)
+ return ret;
+ ctx->pipe = ret;
+
+ possible_crtcs = (1 << ctx->pipe);
+
+ for (nr = 0; nr < VOP_MAX_WIN_SUPPORT; nr++) {
+ if (nr == VOP_DEFAULT_PRIMARY) {
+ primary = rockchip_plane_init(ctx, possible_crtcs,
+ DRM_PLANE_TYPE_PRIMARY,
+ VOP_DEFAULT_PRIMARY);
+ if (IS_ERR(primary)) {
+ DRM_ERROR("fail to init primary plane\n");
+ return PTR_ERR(primary);
+ }
+ } else if (nr == VOP_DEFAULT_CURSOR) {
+ cursor = rockchip_plane_init(ctx, possible_crtcs,
+ DRM_PLANE_TYPE_CURSOR,
+ VOP_DEFAULT_CURSOR);
+ if (IS_ERR(cursor)) {
+ DRM_ERROR("fail to init cursor plane\n");
+ return PTR_ERR(cursor);
+ }
+ } else {
+ struct drm_plane *plane;
+
+ plane = rockchip_plane_init(ctx, possible_crtcs,
+ DRM_PLANE_TYPE_OVERLAY,
+ nr);
+ if (IS_ERR(plane)) {
+ DRM_ERROR("fail to init overlay plane\n");
+ return PTR_ERR(plane);
+ }
+ }
+ }
+
+ drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
+ &rockchip_crtc_funcs);
+ drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
+
+ return 0;
+}
+
+static int rockchip_vop_initial(struct vop_context *ctx)
+{
+ struct vop_driver_data *vop_data = ctx->data;
+ const struct vop_reg_data *init_table = vop_data->init_table;
+ struct reset_control *ahb_rst;
+ int i, ret;
+
+ ctx->hclk = devm_clk_get(ctx->dev, "hclk_vop");
+ if (IS_ERR(ctx->hclk)) {
+ dev_err(ctx->dev, "failed to get hclk source\n");
+ return PTR_ERR(ctx->hclk);
+ }
+ ctx->aclk = devm_clk_get(ctx->dev, "aclk_vop");
+ if (IS_ERR(ctx->aclk)) {
+ dev_err(ctx->dev, "failed to get aclk source\n");
+ return PTR_ERR(ctx->aclk);
+ }
+ ctx->dclk = devm_clk_get(ctx->dev, "dclk_vop");
+ if (IS_ERR(ctx->dclk)) {
+ dev_err(ctx->dev, "failed to get dclk source\n");
+ return PTR_ERR(ctx->dclk);
+ }
+
+ ret = clk_prepare(ctx->hclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to prepare hclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare(ctx->dclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to prepare dclk\n");
+ goto err_unprepare_hclk;
+ }
+
+ ret = clk_prepare(ctx->aclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to prepare aclk\n");
+ goto err_unprepare_dclk;
+ }
+
+ /*
+ * enable hclk, so that we can config vop register.
+ */
+ ret = clk_enable(ctx->hclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to prepare aclk\n");
+ goto err_unprepare_aclk;
+ }
+ /*
+ * do hclk_reset, reset all vop registers.
+ */
+ ahb_rst = devm_reset_control_get(ctx->dev, "ahb");
+ if (IS_ERR(ahb_rst)) {
+ dev_err(ctx->dev, "failed to get ahb reset\n");
+ ret = PTR_ERR(ahb_rst);
+ goto err_disable_hclk;
+ }
+ reset_control_assert(ahb_rst);
+ usleep_range(10, 20);
+ reset_control_deassert(ahb_rst);
+
+ memcpy(ctx->regsbak, ctx->regs, ctx->len);
+
+ for (i = 0; i < vop_data->table_size; i++)
+ vop_writel(ctx, init_table[i].offset, init_table[i].value);
+
+ for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++)
+ VOP_WIN_SET(ctx, vop_data->win[i], enable, 0);
+
+ vop_cfg_done(ctx);
+
+ /*
+ * do dclk_reset, let all config take affect.
+ */
+ ctx->dclk_rst = devm_reset_control_get(ctx->dev, "dclk");
+ if (IS_ERR(ctx->dclk_rst)) {
+ dev_err(ctx->dev, "failed to get dclk reset\n");
+ ret = PTR_ERR(ctx->dclk_rst);
+ goto err_unprepare_aclk;
+ }
+ reset_control_assert(ctx->dclk_rst);
+ usleep_range(10, 20);
+ reset_control_deassert(ctx->dclk_rst);
+
+ clk_disable(ctx->hclk);
+
+ ctx->dpms = DRM_MODE_DPMS_OFF;
+
+ return 0;
+
+err_disable_hclk:
+ clk_disable(ctx->hclk);
+err_unprepare_aclk:
+ clk_unprepare(ctx->aclk);
+err_unprepare_dclk:
+ clk_unprepare(ctx->dclk);
+err_unprepare_hclk:
+ clk_unprepare(ctx->hclk);
+ return ret;
+}
+
+static int vop_bind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vop_driver_data *vop_data = vop_get_driver_data(dev);
+ struct drm_device *drm_dev = data;
+ struct vop_context *ctx;
+ struct resource *res;
+ int ret;
+
+ if (!vop_data)
+ return -ENODEV;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = dev;
+ ctx->data = vop_data;
+ ctx->drm_dev = drm_dev;
+ dev_set_drvdata(dev, ctx);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctx->len = resource_size(res);
+ ctx->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctx->regs))
+ return PTR_ERR(ctx->regs);
+
+ ctx->regsbak = devm_kzalloc(dev, ctx->len, GFP_KERNEL);
+ if (!ctx->regsbak)
+ return -ENOMEM;
+
+ ret = rockchip_vop_initial(ctx);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
+ return ret;
+ }
+
+ ctx->irq = platform_get_irq(pdev, 0);
+ if (ctx->irq < 0) {
+ dev_err(dev, "cannot find irq for vop\n");
+ return ctx->irq;
+ }
+
+ spin_lock_init(&ctx->reg_lock);
+ spin_lock_init(&ctx->irq_lock);
+
+ init_waitqueue_head(&ctx->wait_vsync_queue);
+ atomic_set(&ctx->wait_vsync_event, 0);
+
+ ret = vop_iommu_init(ctx);
+ if (ret) {
+ DRM_ERROR("Failed to setup iommu, %d\n", ret);
+ return ret;
+ }
+
+ ctx->vsync_wq = create_singlethread_workqueue("vsync");
+ if (!ctx->vsync_wq) {
+ dev_err(dev, "failed to create workqueue\n");
+ return -EINVAL;
+ }
+ INIT_WORK(&ctx->vsync_work, rockchip_vsync_worker);
+
+ mutex_init(&ctx->vsync_mutex);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_request_irq(dev, ctx->irq, rockchip_vop_isr,
+ IRQF_SHARED, dev_name(dev), ctx);
+ if (ret) {
+ dev_err(dev, "cannot requeset irq%d - err %d\n", ctx->irq, ret);
+ return ret;
+ }
+
+ return vop_create_crtc(ctx);
+}
+
+static void vop_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct vop_context *ctx = dev_get_drvdata(dev);
+ struct drm_crtc *crtc = &ctx->crtc;
+
+ drm_crtc_cleanup(crtc);
+ pm_runtime_disable(dev);
+ rockchip_drm_remove_crtc(drm_dev, ctx->pipe);
+}
+
+static const struct component_ops vop_component_ops = {
+ .bind = vop_bind,
+ .unbind = vop_unbind,
+};
+
+static int vop_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vop_context *ctx;
+
+ if (!dev->of_node) {
+ dev_err(dev, "can't find vop devices\n");
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, ctx);
+
+ return component_add(dev, &vop_component_ops);
+}
+
+static int vop_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &vop_component_ops);
+
+ return 0;
+}
+
+struct platform_driver rockchip_vop_platform_driver = {
+ .probe = vop_probe,
+ .remove = vop_remove,
+ .driver = {
+ .name = "rockchip-vop",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(vop_driver_dt_match),
+ },
+};
+
+module_platform_driver(rockchip_vop_platform_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP VOP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
new file mode 100644
index 0000000..952317c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_VOP_H
+#define _ROCKCHIP_DRM_VOP_H
+
+/* register definition */
+#define REG_CFG_DONE 0x0000
+#define VERSION_INFO 0x0004
+#define SYS_CTRL 0x0008
+#define SYS_CTRL1 0x000c
+#define DSP_CTRL0 0x0010
+#define DSP_CTRL1 0x0014
+#define DSP_BG 0x0018
+#define MCU_CTRL 0x001c
+#define INTR_CTRL0 0x0020
+#define INTR_CTRL1 0x0024
+#define WIN0_CTRL0 0x0030
+#define WIN0_CTRL1 0x0034
+#define WIN0_COLOR_KEY 0x0038
+#define WIN0_VIR 0x003c
+#define WIN0_YRGB_MST 0x0040
+#define WIN0_CBR_MST 0x0044
+#define WIN0_ACT_INFO 0x0048
+#define WIN0_DSP_INFO 0x004c
+#define WIN0_DSP_ST 0x0050
+#define WIN0_SCL_FACTOR_YRGB 0x0054
+#define WIN0_SCL_FACTOR_CBR 0x0058
+#define WIN0_SCL_OFFSET 0x005c
+#define WIN0_SRC_ALPHA_CTRL 0x0060
+#define WIN0_DST_ALPHA_CTRL 0x0064
+#define WIN0_FADING_CTRL 0x0068
+/* win1 register */
+#define WIN1_CTRL0 0x0070
+#define WIN1_CTRL1 0x0074
+#define WIN1_COLOR_KEY 0x0078
+#define WIN1_VIR 0x007c
+#define WIN1_YRGB_MST 0x0080
+#define WIN1_CBR_MST 0x0084
+#define WIN1_ACT_INFO 0x0088
+#define WIN1_DSP_INFO 0x008c
+#define WIN1_DSP_ST 0x0090
+#define WIN1_SCL_FACTOR_YRGB 0x0094
+#define WIN1_SCL_FACTOR_CBR 0x0098
+#define WIN1_SCL_OFFSET 0x009c
+#define WIN1_SRC_ALPHA_CTRL 0x00a0
+#define WIN1_DST_ALPHA_CTRL 0x00a4
+#define WIN1_FADING_CTRL 0x00a8
+/* win2 register */
+#define WIN2_CTRL0 0x00b0
+#define WIN2_CTRL1 0x00b4
+#define WIN2_VIR0_1 0x00b8
+#define WIN2_VIR2_3 0x00bc
+#define WIN2_MST0 0x00c0
+#define WIN2_DSP_INFO0 0x00c4
+#define WIN2_DSP_ST0 0x00c8
+#define WIN2_COLOR_KEY 0x00cc
+#define WIN2_MST1 0x00d0
+#define WIN2_DSP_INFO1 0x00d4
+#define WIN2_DSP_ST1 0x00d8
+#define WIN2_SRC_ALPHA_CTRL 0x00dc
+#define WIN2_MST2 0x00e0
+#define WIN2_DSP_INFO2 0x00e4
+#define WIN2_DSP_ST2 0x00e8
+#define WIN2_DST_ALPHA_CTRL 0x00ec
+#define WIN2_MST3 0x00f0
+#define WIN2_DSP_INFO3 0x00f4
+#define WIN2_DSP_ST3 0x00f8
+#define WIN2_FADING_CTRL 0x00fc
+/* win3 register */
+#define WIN3_CTRL0 0x0100
+#define WIN3_CTRL1 0x0104
+#define WIN3_VIR0_1 0x0108
+#define WIN3_VIR2_3 0x010c
+#define WIN3_MST0 0x0110
+#define WIN3_DSP_INFO0 0x0114
+#define WIN3_DSP_ST0 0x0118
+#define WIN3_COLOR_KEY 0x011c
+#define WIN3_MST1 0x0120
+#define WIN3_DSP_INFO1 0x0124
+#define WIN3_DSP_ST1 0x0128
+#define WIN3_SRC_ALPHA_CTRL 0x012c
+#define WIN3_MST2 0x0130
+#define WIN3_DSP_INFO2 0x0134
+#define WIN3_DSP_ST2 0x0138
+#define WIN3_DST_ALPHA_CTRL 0x013c
+#define WIN3_MST3 0x0140
+#define WIN3_DSP_INFO3 0x0144
+#define WIN3_DSP_ST3 0x0148
+#define WIN3_FADING_CTRL 0x014c
+/* hwc register */
+#define HWC_CTRL0 0x0150
+#define HWC_CTRL1 0x0154
+#define HWC_MST 0x0158
+#define HWC_DSP_ST 0x015c
+#define HWC_SRC_ALPHA_CTRL 0x0160
+#define HWC_DST_ALPHA_CTRL 0x0164
+#define HWC_FADING_CTRL 0x0168
+/* post process register */
+#define POST_DSP_HACT_INFO 0x0170
+#define POST_DSP_VACT_INFO 0x0174
+#define POST_SCL_FACTOR_YRGB 0x0178
+#define POST_SCL_CTRL 0x0180
+#define POST_DSP_VACT_INFO_F1 0x0184
+#define DSP_HTOTAL_HS_END 0x0188
+#define DSP_HACT_ST_END 0x018c
+#define DSP_VTOTAL_VS_END 0x0190
+#define DSP_VACT_ST_END 0x0194
+#define DSP_VS_ST_END_F1 0x0198
+#define DSP_VACT_ST_END_F1 0x019c
+/* register definition end */
+
+/* interrupt define */
+#define DSP_HOLD_VALID_INTR (1 << 0)
+#define FS_INTR (1 << 1)
+#define LINE_FLAG_INTR (1 << 2)
+#define BUS_ERROR_INTR (1 << 3)
+
+#define DSP_HOLD_VALID_INTR_EN(x) ((x) << 4)
+#define FS_INTR_EN(x) ((x) << 5)
+#define LINE_FLAG_INTR_EN(x) ((x) << 6)
+#define BUS_ERROR_INTR_EN(x) ((x) << 7)
+#define DSP_HOLD_VALID_INTR_MASK (1 << 4)
+#define FS_INTR_MASK (1 << 5)
+#define LINE_FLAG_INTR_MASK (1 << 6)
+#define BUS_ERROR_INTR_MASK (1 << 7)
+
+#define DSP_HOLD_VALID_INTR_CLR (1 << 8)
+#define FS_INTR_CLR (1 << 9)
+#define LINE_FLAG_INTR_CLR (1 << 10)
+#define BUS_ERROR_INTR_CLR (1 << 11)
+#define DSP_LINE_NUM(x) (((x) & 0x1fff) << 12)
+#define DSP_LINE_NUM_MASK (0x1fff << 12)
+
+/* src alpha ctrl define */
+#define SRC_FADING_VALUE(x) (((x) & 0xff) << 24)
+#define SRC_GLOBAL_ALPHA(x) (((x) & 0xff) << 16)
+#define SRC_FACTOR_M0(x) (((x) & 0x7) << 6)
+#define SRC_ALPHA_CAL_M0(x) (((x) & 0x1) << 5)
+#define SRC_BLEND_M0(x) (((x) & 0x3) << 3)
+#define SRC_ALPHA_M0(x) (((x) & 0x1) << 2)
+#define SRC_COLOR_M0(x) (((x) & 0x1) << 1)
+#define SRC_ALPHA_EN(x) (((x) & 0x1) << 0)
+/* dst alpha ctrl define */
+#define DST_FACTOR_M0(x) (((x) & 0x7) << 6)
+
+/*
+ * display output interface supported by rockchip lcdc
+ */
+#define ROCKCHIP_OUT_MODE_P888 0
+#define ROCKCHIP_OUT_MODE_P666 1
+#define ROCKCHIP_OUT_MODE_P565 2
+/* for use special outface */
+#define ROCKCHIP_OUT_MODE_AAAA 15
+
+enum alpha_mode {
+ ALPHA_STRAIGHT,
+ ALPHA_INVERSE,
+};
+
+enum global_blend_mode {
+ ALPHA_GLOBAL,
+ ALPHA_PER_PIX,
+ ALPHA_PER_PIX_GLOBAL,
+};
+
+enum alpha_cal_mode {
+ ALPHA_SATURATION,
+ ALPHA_NO_SATURATION,
+};
+
+enum color_mode {
+ ALPHA_SRC_PRE_MUL,
+ ALPHA_SRC_NO_PRE_MUL,
+};
+
+enum factor_mode {
+ ALPHA_ZERO,
+ ALPHA_ONE,
+ ALPHA_SRC,
+ ALPHA_SRC_INVERSE,
+ ALPHA_SRC_GLOBAL,
+};
+
+#endif /* _ROCKCHIP_DRM_VOP_H */
--
1.7.9.5
^ permalink raw reply related
* [PATCH v9 0/3] Add drm driver for Rockchip Socs
From: Mark Yao @ 2014-09-30 12:57 UTC (permalink / raw)
To: heiko-4mtYJXux2i+zQB+pC5nmwQ, 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,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
xxm-TNX95d0MmH7DzftRWevZcw, huangtao-TNX95d0MmH7DzftRWevZcw,
kever.yang-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, Mark Yao
This a series of patches is a DRM Driver for Rockchip Socs, add support
for vop devices. Future patches will add additional encoders/connectors,
such as eDP, HDMI.
The basic "crtc" for rockchip is a "VOP" - Video Output Processor.
the vop devices found on Rockchip rk3288 Soc, rk3288 soc have two similar
Vop devices. Vop devices support iommu mapping, we use dma-mapping API with
ARM_DMA_USE_IOMMU.
Changes in v2:
- add DRM master device node to list all display nodes that comprise
the graphics subsystem.
- 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.
- add vop reset.
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 style 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, instead 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 ling 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.
- fix mail info style.
Mark yao (3):
drm: rockchip: Add basic drm driver
dt-bindings: video: Add for rockchip display subsytem
dt-bindings: video: Add documentation for rockchip vop
.../devicetree/bindings/video/rockchip-drm.txt | 19 +
.../devicetree/bindings/video/rockchip-vop.txt | 58 +
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 | 65 +
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 | 209 +++
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 | 1423 ++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 196 +++
16 files changed, 3103 insertions(+)
--
1.7.9.5
^ permalink raw reply
* Re: [PATCH v3] mm: add mremap flag for preserving the old mapping
From: Daniel Micay @ 2014-09-30 9:36 UTC (permalink / raw)
To: Andy Lutomirski
Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org, Linux API,
Andrew Morton, jasone
In-Reply-To: <CALCETrX6D7X7zm3qCn8kaBtYHCQvdR06LAAwzBA=1GteHAaLKA@mail.gmail.com>
On 30/09/14 01:53 AM, Andy Lutomirski wrote:
> On Mon, Sep 29, 2014 at 9:55 PM, Daniel Micay <danielmicay@gmail.com> wrote:
>> This introduces the MREMAP_RETAIN flag for preserving the source mapping
>> when MREMAP_MAYMOVE moves the pages to a new destination. Accesses to
>> the source location will fault and cause fresh pages to be mapped in.
>>
>> For consistency, the old_len >= new_len case could decommit the pages
>> instead of unmapping. However, userspace can accomplish the same thing
>> via madvise and a coherent definition of the flag is possible without
>> the extra complexity.
>
> IMO this needs very clear documentation of exactly what it does.
Agreed, and thanks for the review. I'll post a slightly modified version
of the patch soon (mostly more commit message changes).
> Does it preserve the contents of the source pages? (If so, why?
> Aren't you wasting a bunch of time on page faults and possibly
> unnecessary COWs?)
The source will act as if it was just created. For an anonymous memory
mapping, it will fault on any accesses and bring in new zeroed pages.
In jemalloc, it replaces an enormous memset(dst, src, size) followed by
madvise(src, size, MADV_DONTNEED) with mremap. Using mremap also ends up
eliding page faults from writes at the destination.
TCMalloc has nearly the same page allocation design, although it tries
to throttle the purging so it won't always gain as much.
> Does it work on file mappings? Can it extend file mappings while it moves them?
It works on file mappings. If a move occurs, there will be the usual
extended destination mapping but with the source mapping left intact.
It wouldn't be useful with existing allocators, but in theory a general
purpose allocator could expose an MMIO API in order to reuse the same
address space via MAP_FIXED/MREMAP_FIXED to reduce VM fragmentation.
> If you MREMAP_RETAIN a partially COWed private mapping, what happens?
The original mapping is zeroed in the following test, as it would be
without fork:
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
size_t size = 1024 * 1024;
char *orig = mmap(NULL, size, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
memset(orig, 5, size);
int pid = fork();
if (pid == -1)
return 1;
if (pid == 0) {
memset(orig, 5, 1024);
char *new = mremap(orig, size, size * 128, MREMAP_MAYMOVE|4);
if (new == orig) return 1;
for (size_t i = 0; i < size; i++)
if (new[i] != 5)
return 1;
for (size_t i = 0; i < size; i++)
if (orig[i] != 0)
return 1;
return 0;
}
int status;
if (wait(&status) < -1) return 1;
if (WIFEXITED(status))
return WEXITSTATUS(status);
return 1;
}
Hopefully this is the case you're referring to. :)
> Does it work on special mappings? If so, please prevent it from doing
> so. mremapping x86's vdso is a thing, and duplicating x86's vdso
> should not become a thing, because x86_32 in particular will become
> extremely confused.
I'll add a check for arch_vma_name(vma) == NULL.
There's an existing check for VM_DONTEXPAND | VM_PFNMAP when expanding
allocations (the only case this flag impacts). Are there other kinds of
special mappings that you're referring to?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH v8 2/3] dt-bindings: video: Add for rockchip display subsytem
From: Daniel Vetter @ 2014-09-30 8:24 UTC (permalink / raw)
To: Mark Yao, heiko-4mtYJXux2i+zQB+pC5nmwQ, Boris BREZILLON,
David Airlie, Rob Clark, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Randy Dunlap, Grant Likely,
Greg Kroah-Hartman, John Stultz, Rom Lemarchand,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw, kfx
In-Reply-To: <20140930081020.GE24667-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
On Tue, Sep 30, 2014 at 10:10:20AM +0200, Daniel Vetter wrote:
> On Tue, Sep 30, 2014 at 02:14:19PM +0800, Mark Yao wrote:
> > From: Mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> >
> > This add a display subsystem comprise the all display interface nodes.
> >
> > Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> > Signed-off-by: Daniel Kurtz <djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> > Acked-by: Daniel Vetter <daniel-/w4YWyX8dFk@public.gmane.org>
> > Reviewed-by: Rob Clark <robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>
> Just a quick clarification: My ack was only about the drm driver itself, I
> try really hard to stay away from DT insanity ;-) So please drop my ack
> from these two patches.
>
> Aside: DT patches need to go through a special process and you must cc dt
> maintainers and mailing lists. Your patch seems to lack this, which afaiui
> is a blocker for merging the driver.
With coffee working I've now noticed that DT people are on cc ;-) I guess
you need to poke them for review, since the drm/driver side looks ready.
Would be sad if this would miss 3.18 imo.
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v8 2/3] dt-bindings: video: Add for rockchip display subsytem
From: Mark yao @ 2014-09-30 8:19 UTC (permalink / raw)
To: heiko-4mtYJXux2i+zQB+pC5nmwQ, Boris BREZILLON, David Airlie,
Rob Clark, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, Randy Dunlap, Grant Likely, Greg Kroah-Hartman,
John Stultz, Rom Lemarchand, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw, cf
In-Reply-To: <20140930081020.GE24667-dv86pmgwkMBes7Z6vYuT8azUEOm+Xw19@public.gmane.org>
On 2014年09月30日 16:10, Daniel Vetter wrote:
> On Tue, Sep 30, 2014 at 02:14:19PM +0800, Mark Yao wrote:
>> From: Mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>
>> This add a display subsystem comprise the all display interface nodes.
>>
>> Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>> Signed-off-by: Daniel Kurtz <djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
>> Acked-by: Daniel Vetter <daniel-/w4YWyX8dFk@public.gmane.org>
>> Reviewed-by: Rob Clark <robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Just a quick clarification: My ack was only about the drm driver itself, I
> try really hard to stay away from DT insanity ;-) So please drop my ack
> from these two patches.
>
> Aside: DT patches need to go through a special process and you must cc dt
> maintainers and mailing lists. Your patch seems to lack this, which afaiui
> is a blocker for merging the driver.
>
> Thanks, Daniel
Oh, I'm sorry, I will drop it soon.
>> ---
>> Changes in v2:
>> - add DRM master device node to list all display nodes that comprise
>> the graphics subsystem.
>>
>> Changes in v3: None
>>
>> Changes in v4: None
>>
>> Changes in v5: None
>>
>> Changes in v6: None
>>
>> Changes in v7: None
>>
>> Changes in v8: None
>>
>> .../devicetree/bindings/video/rockchip-drm.txt | 19 +++++++++++++++++++
>> 1 file changed, 19 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/video/rockchip-drm.txt
>>
>> diff --git a/Documentation/devicetree/bindings/video/rockchip-drm.txt b/Documentation/devicetree/bindings/video/rockchip-drm.txt
>> new file mode 100644
>> index 0000000..7fff582
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/video/rockchip-drm.txt
>> @@ -0,0 +1,19 @@
>> +Rockchip DRM master device
>> +================================
>> +
>> +The Rockchip DRM master device is a virtual device needed to list all
>> +vop devices or other display interface nodes that comprise the
>> +graphics subsystem.
>> +
>> +Required properties:
>> +- compatible: Should be "rockchip,display-subsystem"
>> +- ports: Should contain a list of phandles pointing to display interface port
>> + of vop devices. vop definitions as defined in
>> + Documentation/devicetree/bindings/video/rockchip-vop.txt
>> +
>> +example:
>> +
>> +display-subsystem {
>> + compatible = "rockchip,display-subsystem";
>> + ports = <&vopl_out>, <&vopb_out>;
>> +};
>> --
>> 1.7.9.5
>>
>>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v8 2/3] dt-bindings: video: Add for rockchip display subsytem
From: Heiko Stübner @ 2014-09-30 8:16 UTC (permalink / raw)
To: Daniel Vetter
Cc: Mark Yao, Boris BREZILLON, David Airlie, Rob Clark, Rob Herring,
Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Randy Dunlap,
Grant Likely, Greg Kroah-Hartman, John Stultz, Rom Lemarchand,
devicetree, linux-doc, linux-kernel, dri-devel, linux-api,
linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq, kfx,
cym
In-Reply-To: <20140930081020.GE24667@phenom.ffwll.local>
Am Dienstag, 30. September 2014, 10:10:20 schrieb Daniel Vetter:
> On Tue, Sep 30, 2014 at 02:14:19PM +0800, Mark Yao wrote:
> > From: Mark yao <mark.yao@rock-chips.com>
> >
> > This add a display subsystem comprise the all display interface nodes.
> >
> > Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
> > Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
> > Acked-by: Daniel Vetter <daniel@ffwll.ch>
> > Reviewed-by: Rob Clark <robdclark@gmail.com>
>
> Just a quick clarification: My ack was only about the drm driver itself, I
> try really hard to stay away from DT insanity ;-) So please drop my ack
> from these two patches.
>
> Aside: DT patches need to go through a special process and you must cc dt
> maintainers and mailing lists. Your patch seems to lack this, which afaiui
> is a blocker for merging the driver.
Looking at the recipients I see the dt list (devicetree@vger.kernel.org) as
well as the dt maintainers (Rob Herring, Pawel Moll, Mark Rutland, Ian
Campbell and Kumar Gala) listed there. So from this side it should be fine
Heiko
^ permalink raw reply
* Re: [PATCH v8 2/3] dt-bindings: video: Add for rockchip display subsytem
From: Daniel Vetter @ 2014-09-30 8:10 UTC (permalink / raw)
To: Mark Yao
Cc: heiko-4mtYJXux2i+zQB+pC5nmwQ, 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,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw, kfx
In-Reply-To: <1412057659-19483-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
On Tue, Sep 30, 2014 at 02:14:19PM +0800, Mark Yao wrote:
> From: Mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>
> This add a display subsystem comprise the all display interface nodes.
>
> Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> Signed-off-by: Daniel Kurtz <djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> Acked-by: Daniel Vetter <daniel-/w4YWyX8dFk@public.gmane.org>
> Reviewed-by: Rob Clark <robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Just a quick clarification: My ack was only about the drm driver itself, I
try really hard to stay away from DT insanity ;-) So please drop my ack
from these two patches.
Aside: DT patches need to go through a special process and you must cc dt
maintainers and mailing lists. Your patch seems to lack this, which afaiui
is a blocker for merging the driver.
Thanks, Daniel
> ---
> Changes in v2:
> - add DRM master device node to list all display nodes that comprise
> the graphics subsystem.
>
> Changes in v3: None
>
> Changes in v4: None
>
> Changes in v5: None
>
> Changes in v6: None
>
> Changes in v7: None
>
> Changes in v8: None
>
> .../devicetree/bindings/video/rockchip-drm.txt | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/video/rockchip-drm.txt
>
> diff --git a/Documentation/devicetree/bindings/video/rockchip-drm.txt b/Documentation/devicetree/bindings/video/rockchip-drm.txt
> new file mode 100644
> index 0000000..7fff582
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/video/rockchip-drm.txt
> @@ -0,0 +1,19 @@
> +Rockchip DRM master device
> +================================
> +
> +The Rockchip DRM master device is a virtual device needed to list all
> +vop devices or other display interface nodes that comprise the
> +graphics subsystem.
> +
> +Required properties:
> +- compatible: Should be "rockchip,display-subsystem"
> +- ports: Should contain a list of phandles pointing to display interface port
> + of vop devices. vop definitions as defined in
> + Documentation/devicetree/bindings/video/rockchip-vop.txt
> +
> +example:
> +
> +display-subsystem {
> + compatible = "rockchip,display-subsystem";
> + ports = <&vopl_out>, <&vopb_out>;
> +};
> --
> 1.7.9.5
>
>
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
^ permalink raw reply
* [PATCH v8 3/3] dt-bindings: video: Add documentation for rockchip vop
From: Mark Yao @ 2014-09-30 6:15 UTC (permalink / raw)
To: heiko-4mtYJXux2i+zQB+pC5nmwQ, 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,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
xxm-TNX95d0MmH7DzftRWevZcw, huangtao-TNX95d0MmH7DzftRWevZcw,
kever.yang-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, Mark yao
In-Reply-To: <1412057386-19375-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
From: Mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
This adds binding documentation for Rockchip SoC VOP driver.
Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Acked-by: Daniel Vetter <daniel-/w4YWyX8dFk@public.gmane.org>
Reviewed-by: Rob Clark <robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
Changes in v2:
- rename "lcdc" to "vop"
- add vop reset
- add iommu node
- add port for display-subsystem
Changes in v3: None
Changes in v4: None
Changes in v5: None
Changes in v6: None
Changes in v7: None
Changes in v8: None
.../devicetree/bindings/video/rockchip-vop.txt | 58 ++++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/rockchip-vop.txt
diff --git a/Documentation/devicetree/bindings/video/rockchip-vop.txt b/Documentation/devicetree/bindings/video/rockchip-vop.txt
new file mode 100644
index 0000000..d15351f
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-vop.txt
@@ -0,0 +1,58 @@
+device-tree bindings for rockchip soc display controller (vop)
+
+VOP (Visual Output Processor) is the Display Controller for the Rockchip
+series of SoCs which transfers the image data from a video memory
+buffer to an external LCD interface.
+
+Required properties:
+- compatible: value should be one of the following
+ "rockchip,rk3288-vop";
+
+- interrupts: should contain a list of all VOP IP block interrupts in the
+ order: VSYNC, LCD_SYSTEM. The interrupt specifier
+ format depends on the interrupt controller used.
+
+- clocks: must include clock specifiers corresponding to entries in the
+ clock-names property.
+
+- clock-names: Must contain
+ aclk_vop: for ddr buffer transfer.
+ hclk_vop: for ahb bus to R/W the phy regs.
+ dclk_vop: pixel clock.
+
+- resets: Must contain an entry for each entry in reset-names.
+ See ../reset/reset.txt for details.
+- reset-names: Must include the following entries:
+ - axi
+ - ahb
+ - dclk
+
+- iommus: required a iommu node
+
+- port: A port node with endpoint definitions as defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+SoC specific DT entry:
+ vopb: vopb@ff930000 {
+ compatible = "rockchip,rk3288-vop";
+ reg = <0xff930000 0x19c>;
+ interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>;
+ clock-names = "aclk_vop", "dclk_vop", "hclk_vop";
+ resets = <&cru SRST_LCDC1_AXI>, <&cru SRST_LCDC1_AHB>, <&cru SRST_LCDC1_DCLK>;
+ reset-names = "axi", "ahb", "dclk";
+ iommus = <&vopb_mmu>;
+ vopb_out: port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ vopb_out_edp: endpoint@0 {
+ reg = <0>;
+ remote-endpoint=<&edp_in_vopb>;
+ };
+ vopb_out_hdmi: endpoint@1 {
+ reg = <1>;
+ remote-endpoint=<&hdmi_in_vopb>;
+ };
+ };
+ };
--
1.7.9.5
^ permalink raw reply related
* [PATCH v8 2/3] dt-bindings: video: Add for rockchip display subsytem
From: Mark Yao @ 2014-09-30 6:14 UTC (permalink / raw)
To: heiko-4mtYJXux2i+zQB+pC5nmwQ, 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,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-api-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
dianders-F7+t8E8rja9g9hUCZPvPmw, marcheu-F7+t8E8rja9g9hUCZPvPmw,
dbehr-F7+t8E8rja9g9hUCZPvPmw, olof-nZhT3qVonbNeoWH0uzbU5w,
djkurtz-F7+t8E8rja9g9hUCZPvPmw, xjq-TNX95d0MmH7DzftRWevZcw,
kfx-TNX95d0MmH7DzftRWevZcw, cym-TNX95d0MmH7DzftRWevZcw,
cf-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
xxm-TNX95d0MmH7DzftRWevZcw, huangtao-TNX95d0MmH7DzftRWevZcw,
kever.yang-TNX95d0MmH7DzftRWevZcw, yxj-TNX95d0MmH7DzftRWevZcw,
wxt-TNX95d0MmH7DzftRWevZcw, xw-TNX95d0MmH7DzftRWevZcw, Mark yao
In-Reply-To: <1412057386-19375-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
From: Mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
This add a display subsystem comprise the all display interface nodes.
Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Daniel Kurtz <djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Acked-by: Daniel Vetter <daniel-/w4YWyX8dFk@public.gmane.org>
Reviewed-by: Rob Clark <robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
Changes in v2:
- add DRM master device node to list all display nodes that comprise
the graphics subsystem.
Changes in v3: None
Changes in v4: None
Changes in v5: None
Changes in v6: None
Changes in v7: None
Changes in v8: None
.../devicetree/bindings/video/rockchip-drm.txt | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/rockchip-drm.txt
diff --git a/Documentation/devicetree/bindings/video/rockchip-drm.txt b/Documentation/devicetree/bindings/video/rockchip-drm.txt
new file mode 100644
index 0000000..7fff582
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-drm.txt
@@ -0,0 +1,19 @@
+Rockchip DRM master device
+================================
+
+The Rockchip DRM master device is a virtual device needed to list all
+vop devices or other display interface nodes that comprise the
+graphics subsystem.
+
+Required properties:
+- compatible: Should be "rockchip,display-subsystem"
+- ports: Should contain a list of phandles pointing to display interface port
+ of vop devices. vop definitions as defined in
+ Documentation/devicetree/bindings/video/rockchip-vop.txt
+
+example:
+
+display-subsystem {
+ compatible = "rockchip,display-subsystem";
+ ports = <&vopl_out>, <&vopb_out>;
+};
--
1.7.9.5
^ permalink raw reply related
* [PATCH v8 1/3] drm: rockchip: Add basic drm driver
From: Mark Yao @ 2014-09-30 6:12 UTC (permalink / raw)
To: heiko, 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, linux-doc, linux-kernel, dri-devel, linux-api,
linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq, kfx,
cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw, Mark yao
In-Reply-To: <1412057386-19375-1-git-send-email-mark.yao@rock-chips.com>
From: Mark yao <mark.yao@rock-chips.com>
This patch adds the basic structure of a DRM Driver for Rockchip Socs.
Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Daniel Vetter <daniel@ffwll.ch>
Reviewed-by: Rob Clark <robdclark@gmail.com>
---
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 style 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, instead 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 ling 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
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 | 65 ++
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 | 209 ++++
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 | 1438 +++++++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 196 ++++
14 files changed, 3041 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)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
+obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
obj-$(CONFIG_DRM_GMA500) += gma500/
obj-$(CONFIG_DRM_UDL) += udl/
obj-$(CONFIG_DRM_AST) += ast/
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
new file mode 100644
index 0000000..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/rockchip/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 := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
+ rockchip_drm_gem.o rockchip_drm_vop.o
+
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
new file mode 100644
index 0000000..879b2e0
--- /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 <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/dma-iommu.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_graph.h>
+#include <linux/component.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+
+#define DRIVER_NAME "rockchip"
+#define DRIVER_DESC "RockChip Soc DRM"
+#define DRIVER_DATE "20140818"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+/*
+ * Attach a (component) device to the shared drm dma mapping from master drm
+ * device. This is used by the VOPs to map GEM buffers to a common DMA
+ * mapping.
+ */
+int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
+ struct device *dev)
+{
+ struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
+ int ret;
+
+ ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ dma_set_max_seg_size(dev, 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 long flags)
+{
+ struct rockchip_drm_private *private;
+ struct dma_iommu_mapping *mapping;
+ struct device *dev = drm_dev->dev;
+ int ret;
+
+ private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
+ if (!private)
+ return -ENOMEM;
+
+ drm_dev->dev_private = private;
+
+ drm_mode_config_init(drm_dev);
+
+ rockchip_drm_mode_config_init(drm_dev);
+
+ dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
+ GFP_KERNEL);
+ if (!dev->dma_parms) {
+ ret = -ENOMEM;
+ goto err_config_cleanup;
+ }
+
+ /* TODO(djkurtz): fetch the mapping start/size from somewhere */
+ mapping = arm_iommu_create_mapping(&platform_bus_type, 0x00000000,
+ SZ_2G);
+ if (IS_ERR(mapping)) {
+ ret = PTR_ERR(mapping);
+ goto err_config_cleanup;
+ }
+
+ ret = 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 = arm_iommu_attach_device(dev, mapping);
+ if (ret)
+ goto err_release_mapping;
+
+ /* Try to bind all sub drivers. */
+ ret = component_bind_all(dev, drm_dev);
+ if (ret)
+ goto err_detach_device;
+
+ /* init kms poll for handling hpd */
+ drm_kms_helper_poll_init(drm_dev);
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = true, we can use the vblank feature.
+ */
+ drm_dev->irq_enabled = true;
+
+ /*
+ * with vblank_disable_allowed = true, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ drm_dev->vblank_disable_allowed = true;
+
+ ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
+ if (ret)
+ goto err_kms_helper_poll_fini;
+
+ rockchip_drm_fbdev_init(drm_dev);
+
+ /* force connectors detection */
+ drm_helper_hpd_irq_event(drm_dev);
+
+ return 0;
+
+err_kms_helper_poll_fini:
+ drm_kms_helper_poll_fini(drm_dev);
+ component_unbind_all(dev, drm_dev);
+err_detach_device:
+ arm_iommu_detach_device(dev);
+err_release_mapping:
+ arm_iommu_release_mapping(dev->archdata.mapping);
+err_config_cleanup:
+ drm_mode_config_cleanup(drm_dev);
+ drm_dev->dev_private = NULL;
+ return ret;
+}
+
+static int rockchip_drm_unload(struct drm_device *drm_dev)
+{
+ struct device *dev = drm_dev->dev;
+
+ drm_kms_helper_poll_fini(drm_dev);
+ component_unbind_all(dev, drm_dev);
+ arm_iommu_detach_device(dev);
+ arm_iommu_release_mapping(dev->archdata.mapping);
+ drm_mode_config_cleanup(drm_dev);
+ drm_dev->dev_private = NULL;
+
+ return 0;
+}
+
+void rockchip_drm_lastclose(struct drm_device *dev)
+{
+ struct rockchip_drm_private *priv = dev->dev_private;
+
+ drm_fb_helper_restore_fbdev_mode_unlocked(&priv->fbdev_helper);
+}
+
+static const struct file_operations rockchip_drm_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = rockchip_gem_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
+ .release = drm_release,
+};
+
+const struct vm_operations_struct rockchip_drm_vm_ops = {
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static struct drm_driver rockchip_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
+ .load = rockchip_drm_load,
+ .unload = rockchip_drm_unload,
+ .lastclose = rockchip_drm_lastclose,
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = rockchip_drm_crtc_enable_vblank,
+ .disable_vblank = rockchip_drm_crtc_disable_vblank,
+ .gem_vm_ops = &rockchip_drm_vm_ops,
+ .gem_free_object = rockchip_gem_free_object,
+ .dumb_create = rockchip_gem_dumb_create,
+ .dumb_map_offset = rockchip_gem_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
+ .gem_prime_vmap = rockchip_gem_prime_vmap,
+ .gem_prime_vunmap = rockchip_gem_prime_vunmap,
+ .fops = &rockchip_drm_driver_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
+{
+ struct drm_connector *connector;
+
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ int old_dpms = connector->dpms;
+
+ if (connector->funcs->dpms)
+ connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
+ /* Set the old mode back to the connector for resume */
+ connector->dpms = old_dpms;
+ }
+ drm_modeset_unlock_all(dev);
+
+ return 0;
+}
+
+static int rockchip_drm_resume(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+
+ drm_modeset_lock_all(dev);
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->funcs->dpms)
+ connector->funcs->dpms(connector, connector->dpms);
+ }
+ drm_modeset_unlock_all(dev);
+
+ drm_helper_resume_force_mode(dev);
+
+ return 0;
+}
+
+static int rockchip_drm_sys_suspend(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+ pm_message_t message;
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ message.event = PM_EVENT_SUSPEND;
+
+ return rockchip_drm_suspend(drm_dev, message);
+}
+
+static int rockchip_drm_sys_resume(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+ if (!pm_runtime_suspended(dev))
+ return 0;
+
+ return rockchip_drm_resume(drm_dev);
+}
+#endif
+
+static const struct dev_pm_ops rockchip_drm_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
+ rockchip_drm_sys_resume)
+};
+
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
+ struct device_node *np)
+{
+ struct rockchip_drm_private *priv = drm->dev_private;
+ struct device_node *port;
+ int pipe;
+
+ if (priv->num_pipe >= ROCKCHIP_MAX_CRTC)
+ return -EINVAL;
+
+ port = of_get_child_by_name(np, "port");
+ if (!port) {
+ dev_err(drm->dev, "no port node found in %s\n",
+ np->full_name);
+ return -ENXIO;
+ }
+ pipe = priv->num_pipe++;
+ crtc->port = port;
+
+ priv->crtc[pipe] = crtc;
+
+ return pipe;
+}
+
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe)
+{
+ struct rockchip_drm_private *priv = drm->dev_private;
+
+ priv->num_pipe--;
+ of_node_put(priv->crtc[pipe]->port);
+ priv->crtc[pipe] = NULL;
+}
+
+struct drm_crtc *rockchip_drm_find_crtc(struct drm_device *drm, int pipe)
+{
+ struct rockchip_drm_private *priv = drm->dev_private;
+
+ if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe])
+ return priv->crtc[pipe];
+
+ return NULL;
+}
+
+/*
+ * @node: device tree node containing encoder input ports
+ * @encoder: drm_encoder
+ */
+int rockchip_drm_encoder_get_mux_id(struct device_node *node,
+ struct drm_encoder *encoder)
+{
+ struct device_node *ep = NULL;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct of_endpoint endpoint;
+ struct device_node *port;
+ int ret;
+
+ if (!node || !crtc)
+ return -EINVAL;
+
+ do {
+ ep = of_graph_get_next_endpoint(node, ep);
+ if (!ep)
+ break;
+
+ port = of_graph_get_remote_port(ep);
+ of_node_put(port);
+ if (port == crtc->port) {
+ ret = of_graph_parse_endpoint(ep, &endpoint);
+ return ret ?: endpoint.id;
+ }
+ } while (ep);
+
+ return -EINVAL;
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+ struct device_node *np = data;
+
+ return dev->of_node == np;
+}
+
+static void rockchip_add_endpoints(struct device *dev,
+ struct component_match **match,
+ struct device_node *port)
+{
+ struct device_node *ep, *remote;
+
+ for_each_child_of_node(port, ep) {
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote || !of_device_is_available(remote)) {
+ of_node_put(remote);
+ continue;
+ } else if (!of_device_is_available(remote->parent)) {
+ dev_warn(dev, "parent device of %s is not available\n",
+ remote->full_name);
+ of_node_put(remote);
+ continue;
+ }
+
+ component_match_add(dev, match, compare_of, remote);
+ of_node_put(remote);
+ }
+}
+
+static int rockchip_drm_bind(struct device *dev)
+{
+ struct drm_device *drm;
+ int ret;
+
+ drm = drm_dev_alloc(&rockchip_drm_driver, dev);
+ if (!drm)
+ return -ENOMEM;
+
+ ret = drm_dev_set_unique(drm, "%s", dev_name(dev));
+ if (ret)
+ goto err_free;
+
+ ret = drm_dev_register(drm, 0);
+ if (ret)
+ goto err_free;
+
+ dev_set_drvdata(dev, drm);
+
+ return 0;
+
+err_free:
+ drm_dev_unref(drm);
+ return ret;
+}
+
+static void rockchip_drm_unbind(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+
+ drm_dev_unregister(drm);
+ drm_dev_unref(drm);
+}
+
+static const struct component_master_ops rockchip_drm_ops = {
+ .bind = rockchip_drm_bind,
+ .unbind = rockchip_drm_unbind,
+};
+
+static int rockchip_drm_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct component_match *match = NULL;
+ struct device_node *np = dev->of_node;
+ struct device_node *port;
+ int i;
+
+ if (!np)
+ return -ENODEV;
+ /*
+ * Bind the crtc ports first, so that
+ * drm_of_find_possible_crtcs called from encoder .bind callbacks
+ * works as expected.
+ */
+ for (i = 0;; i++) {
+ port = of_parse_phandle(np, "ports", i);
+ if (!port)
+ break;
+
+ 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 == 0) {
+ dev_err(dev, "missing 'ports' property\n");
+ return -ENODEV;
+ }
+ /*
+ * For each bound crtc, bind the encoders attached to its
+ * remote endpoint.
+ */
+ for (i = 0;; i++) {
+ port = of_parse_phandle(np, "ports", i);
+ if (!port)
+ break;
+
+ 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, match);
+}
+
+static int rockchip_drm_platform_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &rockchip_drm_ops);
+
+ return 0;
+}
+
+static const struct of_device_id rockchip_drm_dt_ids[] = {
+ { .compatible = "rockchip,display-subsystem", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
+
+static struct platform_driver rockchip_drm_platform_driver = {
+ .probe = rockchip_drm_platform_probe,
+ .remove = rockchip_drm_platform_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rockchip-drm",
+ .of_match_table = rockchip_drm_dt_ids,
+ .pm = &rockchip_drm_pm_ops,
+ },
+};
+
+module_platform_driver(rockchip_drm_platform_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
+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..1ed9aae
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * based on exynos_drm_drv.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_DRV_H
+#define _ROCKCHIP_DRM_DRV_H
+
+#include <drm/drm_fb_helper.h>
+
+#include <linux/module.h>
+#include <linux/component.h>
+
+#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_crtc.
+ * @num_pipe: number of pipes for this device.
+ */
+struct rockchip_drm_private {
+ struct drm_fb_helper fbdev_helper;
+ struct drm_gem_object *fbdev_bo;
+ /*
+ * created crtc object would be contained at this array and
+ * this array is used to be aware of which crtc did it request vblank.
+ */
+ struct drm_crtc *crtc[ROCKCHIP_MAX_CRTC];
+
+ unsigned int num_pipe;
+};
+
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
+ struct device_node *port);
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe);
+struct drm_crtc *rockchip_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 connector_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 pipe);
+int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
+ struct device *dev);
+void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
+ struct device *dev);
+
+#endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
new file mode 100644
index 0000000..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 <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
+
+struct rockchip_drm_fb {
+ struct drm_framebuffer fb;
+ struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
+};
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+ unsigned int plane)
+{
+ struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
+
+ if (plane >= ROCKCHIP_MAX_FB_BUFFER)
+ return NULL;
+
+ return rk_fb->obj[plane];
+}
+
+static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+ struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+ struct drm_gem_object *obj;
+ int i;
+
+ for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
+ obj = rockchip_fb->obj[i];
+ if (obj)
+ drm_gem_object_unreference_unlocked(obj);
+ }
+
+ drm_framebuffer_cleanup(fb);
+ kfree(rockchip_fb);
+}
+
+static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle)
+{
+ struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
+
+ return drm_gem_handle_create(file_priv,
+ rockchip_fb->obj[0], handle);
+}
+
+static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
+ .destroy = rockchip_drm_fb_destroy,
+ .create_handle = rockchip_drm_fb_create_handle,
+};
+
+static struct rockchip_drm_fb *
+rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object **obj, unsigned int num_planes)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+ int ret;
+ int i;
+
+ rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
+ if (!rockchip_fb)
+ return ERR_PTR(-ENOMEM);
+
+ drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
+
+ for (i = 0; i < num_planes; i++)
+ rockchip_fb->obj[i] = obj[i];
+
+ ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
+ &rockchip_drm_fb_funcs);
+ if (ret) {
+ dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
+ ret);
+ kfree(rockchip_fb);
+ return ERR_PTR(ret);
+ }
+
+ return rockchip_fb;
+}
+
+static struct drm_framebuffer *
+rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+ struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+ struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
+ struct drm_gem_object *obj;
+ unsigned int hsub;
+ unsigned int vsub;
+ int num_planes;
+ int ret;
+ int i;
+
+ hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
+ vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
+ num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
+ ROCKCHIP_MAX_FB_BUFFER);
+
+ for (i = 0; i < num_planes; i++) {
+ unsigned int width = mode_cmd->width / (i ? hsub : 1);
+ unsigned int height = mode_cmd->height / (i ? vsub : 1);
+ unsigned int min_size;
+
+ obj = drm_gem_object_lookup(dev, file_priv,
+ mode_cmd->handles[i]);
+ if (!obj) {
+ dev_err(dev->dev, "Failed to lookup GEM object\n");
+ ret = -ENXIO;
+ goto err_gem_object_unreference;
+ }
+
+ min_size = (height - 1) * mode_cmd->pitches[i] +
+ mode_cmd->offsets[i] +
+ width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
+
+ if (obj->size < min_size) {
+ drm_gem_object_unreference_unlocked(obj);
+ ret = -EINVAL;
+ goto err_gem_object_unreference;
+ }
+ objs[i] = obj;
+ }
+
+ rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
+ if (IS_ERR(rockchip_fb)) {
+ ret = PTR_ERR(rockchip_fb);
+ goto err_gem_object_unreference;
+ }
+
+ return &rockchip_fb->fb;
+
+err_gem_object_unreference:
+ for (i--; i >= 0; i--)
+ drm_gem_object_unreference_unlocked(objs[i]);
+ return ERR_PTR(ret);
+}
+
+static void rockchip_drm_output_poll_changed(struct drm_device *dev)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct drm_fb_helper *fb_helper = &private->fbdev_helper;
+
+ drm_fb_helper_hotplug_event(fb_helper);
+}
+
+static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
+ .fb_create = rockchip_user_fb_create,
+ .output_poll_changed = rockchip_drm_output_poll_changed,
+};
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj)
+{
+ struct rockchip_drm_fb *rockchip_fb;
+
+ rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
+ if (IS_ERR(rockchip_fb))
+ return NULL;
+
+ return &rockchip_fb->fb;
+}
+
+void rockchip_drm_mode_config_init(struct drm_device *dev)
+{
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+
+ /*
+ * set max width and height as default value(4096x4096).
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+
+ dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
new file mode 100644
index 0000000..09574d4
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_FB_H
+#define _ROCKCHIP_DRM_FB_H
+
+struct drm_framebuffer *
+rockchip_drm_framebuffer_init(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj);
+void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
+
+void rockchip_drm_mode_config_init(struct drm_device *dev);
+
+struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
+ unsigned int plane);
+#endif /* _ROCKCHIP_DRM_FB_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
new file mode 100644
index 0000000..72fa02c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#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 = info->par;
+ struct rockchip_drm_private *private = to_drm_private(helper);
+
+ return rockchip_gem_mmap_buf(private->fbdev_bo, vma);
+}
+
+static struct fb_ops rockchip_drm_fbdev_ops = {
+ .owner = THIS_MODULE,
+ .fb_mmap = rockchip_fbdev_mmap,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct rockchip_drm_private *private = to_drm_private(helper);
+ struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+ struct drm_device *dev = helper->dev;
+ struct rockchip_gem_object *rk_obj;
+ struct drm_framebuffer *fb;
+ unsigned int bytes_per_pixel;
+ unsigned long offset;
+ struct fb_info *fbi;
+ size_t size;
+ int ret;
+
+ bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+
+ size = mode_cmd.pitches[0] * mode_cmd.height;
+
+ rk_obj = rockchip_gem_create_object(dev, size);
+ if (IS_ERR(rk_obj))
+ return -ENOMEM;
+
+ private->fbdev_bo = &rk_obj->base;
+
+ fbi = framebuffer_alloc(0, dev->dev);
+ if (!fbi) {
+ dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
+ ret = -ENOMEM;
+ goto err_rockchip_gem_free_object;
+ }
+
+ helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd,
+ private->fbdev_bo);
+ if (IS_ERR(helper->fb)) {
+ dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
+ ret = PTR_ERR(helper->fb);
+ goto err_framebuffer_release;
+ }
+
+ helper->fbdev = fbi;
+
+ fbi->par = helper;
+ fbi->flags = FBINFO_FLAG_DEFAULT;
+ fbi->fbops = &rockchip_drm_fbdev_ops;
+
+ ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
+ if (ret) {
+ dev_err(dev->dev, "Failed to allocate color map.\n");
+ goto err_drm_framebuffer_unref;
+ }
+
+ fb = helper->fb;
+ drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
+ drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
+
+ offset = fbi->var.xoffset * bytes_per_pixel;
+ offset += fbi->var.yoffset * fb->pitches[0];
+
+ dev->mode_config.fb_base = 0;
+ fbi->screen_base = rk_obj->kvaddr + offset;
+ fbi->screen_size = rk_obj->base.size;
+ fbi->fix.smem_len = rk_obj->base.size;
+
+ DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
+ fb->width, fb->height, fb->depth, rk_obj->kvaddr,
+ offset, size);
+ return 0;
+
+err_drm_framebuffer_unref:
+ drm_framebuffer_unreference(helper->fb);
+err_framebuffer_release:
+ framebuffer_release(fbi);
+err_rockchip_gem_free_object:
+ rockchip_gem_free_object(&rk_obj->base);
+ return ret;
+}
+
+static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
+ .fb_probe = rockchip_drm_fbdev_create,
+};
+
+int rockchip_drm_fbdev_init(struct drm_device *dev)
+{
+ struct rockchip_drm_private *private = dev->dev_private;
+ struct 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 = dev->mode_config.num_crtc;
+
+ helper = &private->fbdev_helper;
+ helper->funcs = &rockchip_drm_fb_helper_funcs;
+
+ ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n",
+ ret);
+ return ret;
+ }
+
+ ret = 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 = 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 = dev->dev_private;
+ struct drm_fb_helper *helper;
+
+ helper = &private->fbdev_helper;
+
+ if (helper->fbdev) {
+ struct fb_info *info;
+ int ret;
+
+ info = helper->fbdev;
+ ret = unregister_framebuffer(info);
+ if (ret < 0)
+ DRM_DEBUG_KMS("failed unregister_framebuffer() - %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 <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_FBDEV_H
+#define _ROCKCHIP_DRM_FBDEV_H
+
+int rockchip_drm_fbdev_init(struct drm_device *dev);
+
+#endif /* _ROCKCHIP_DRM_FBDEV_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
new file mode 100644
index 0000000..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 <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_vma_manager.h>
+
+#include <linux/anon_inodes.h>
+#include <linux/dma-attrs.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_gem.h"
+
+static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj)
+{
+ struct drm_gem_object *obj = &rk_obj->base;
+ struct drm_device *drm = obj->dev;
+
+ init_dma_attrs(&rk_obj->dma_attrs);
+ dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
+
+ /* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
+ rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
+ &rk_obj->dma_addr, GFP_KERNEL,
+ &rk_obj->dma_attrs);
+ if (IS_ERR(rk_obj->kvaddr)) {
+ int ret = PTR_ERR(rk_obj->kvaddr);
+
+ DRM_ERROR("failed to allocate %#x byte dma buffer, %d",
+ obj->size, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
+{
+ struct drm_gem_object *obj = &rk_obj->base;
+ struct drm_device *drm = obj->dev;
+
+ dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
+ &rk_obj->dma_attrs);
+}
+
+int rockchip_gem_mmap_buf(struct drm_gem_object *obj,
+ struct vm_area_struct *vma)
+{
+ struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+ struct drm_device *drm = obj->dev;
+ unsigned long vm_size;
+
+ vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+ vm_size = vma->vm_end - vma->vm_start;
+
+ if (vm_size > obj->size)
+ return -EINVAL;
+
+ return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
+ obj->size, &rk_obj->dma_attrs);
+}
+
+/* drm driver mmap file operations */
+int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_gem_object *obj;
+ struct drm_vma_offset_node *node;
+ int ret;
+
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
+ mutex_lock(&dev->struct_mutex);
+
+ node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
+ vma->vm_pgoff,
+ vma_pages(vma));
+ if (!node) {
+ mutex_unlock(&dev->struct_mutex);
+ DRM_ERROR("failed to find vma node.\n");
+ return -EINVAL;
+ } else if (!drm_vma_node_is_allowed(node, filp)) {
+ mutex_unlock(&dev->struct_mutex);
+ return -EACCES;
+ }
+
+ obj = container_of(node, struct drm_gem_object, vma_node);
+ ret = rockchip_gem_mmap_buf(obj, vma);
+
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
+
+struct rockchip_gem_object *
+ rockchip_gem_create_object(struct drm_device *drm, unsigned int size)
+{
+ struct rockchip_gem_object *rk_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ size = round_up(size, PAGE_SIZE);
+
+ rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
+ if (!rk_obj)
+ return ERR_PTR(-ENOMEM);
+
+ obj = &rk_obj->base;
+
+ drm_gem_private_object_init(drm, obj, size);
+
+ ret = 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 callback
+ * function
+ */
+void rockchip_gem_free_object(struct drm_gem_object *obj)
+{
+ struct rockchip_gem_object *rk_obj;
+
+ drm_gem_free_mmap_offset(obj);
+
+ rk_obj = to_rockchip_obj(obj);
+
+ rockchip_gem_free_buf(rk_obj);
+
+ kfree(rk_obj);
+}
+
+/*
+ * rockchip_gem_create_with_handle - allocate an object with the given
+ * size and create a gem handle on it
+ *
+ * returns a struct rockchip_gem_object* on success or ERR_PTR values
+ * on failure.
+ */
+static struct rockchip_gem_object *
+rockchip_gem_create_with_handle(struct drm_file *file_priv,
+ struct drm_device *drm, unsigned int size,
+ unsigned int *handle)
+{
+ struct rockchip_gem_object *rk_obj;
+ struct drm_gem_object *obj;
+ int ret;
+
+ rk_obj = rockchip_gem_create_object(drm, size);
+ if (IS_ERR(rk_obj))
+ return ERR_CAST(rk_obj);
+
+ obj = &rk_obj->base;
+
+ /*
+ * allocate a id of idr table where the obj is registered
+ * and handle has the id what user can see.
+ */
+ ret = drm_gem_handle_create(file_priv, obj, handle);
+ if (ret)
+ goto err_handle_create;
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_unreference_unlocked(obj);
+
+ return rk_obj;
+
+err_handle_create:
+ rockchip_gem_free_object(obj);
+
+ return ERR_PTR(ret);
+}
+
+int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset)
+{
+ struct drm_gem_object *obj;
+ int ret;
+
+ mutex_lock(&dev->struct_mutex);
+
+ obj = drm_gem_object_lookup(dev, file_priv, handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret)
+ goto out;
+
+ *offset = drm_vma_node_offset_addr(&obj->vma_node);
+ DRM_DEBUG_KMS("offset = 0x%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 callback
+ * function
+ *
+ * This aligns the pitch and size arguments to the minimum required. wrap
+ * this into your own function if you need bigger alignment.
+ */
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct rockchip_gem_object *rk_obj;
+ int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+ /*
+ * align to 64 bytes since Mali requires it.
+ */
+ min_pitch = ALIGN(min_pitch, 64);
+
+ if (args->pitch < min_pitch)
+ args->pitch = min_pitch;
+
+ if (args->size < args->pitch * args->height)
+ args->size = args->pitch * args->height;
+
+ rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
+ &args->handle);
+
+ return PTR_ERR_OR_ZERO(rk_obj);
+}
+
+/*
+ * Allocate a sg_table for this GEM object.
+ * Note: Both the table's contents, and the sg_table itself must be freed by
+ * the caller.
+ * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
+ */
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
+{
+ struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+ struct drm_device *drm = obj->dev;
+ struct sg_table *sgt;
+ int ret;
+
+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return ERR_PTR(-ENOMEM);
+
+ ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
+ rk_obj->dma_addr, obj->size,
+ &rk_obj->dma_attrs);
+ if (ret) {
+ DRM_ERROR("failed to allocate sgt, %d\n", ret);
+ kfree(sgt);
+ return ERR_PTR(ret);
+ }
+
+ return sgt;
+}
+
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
+{
+ struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+
+ return rk_obj->kvaddr;
+}
+
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+ /* Nothing to do */
+}
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..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 <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_GEM_H
+#define _ROCKCHIP_DRM_GEM_H
+
+#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
+
+struct rockchip_gem_object {
+ struct drm_gem_object base;
+ unsigned int flags;
+
+ void *kvaddr;
+ dma_addr_t dma_addr;
+ struct dma_attrs dma_attrs;
+};
+
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
+struct drm_gem_object *
+rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
+ struct sg_table *sgt);
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+
+/* 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 size);
+
+void rockchip_gem_free_object(struct drm_gem_object *obj);
+
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset);
+#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..f0e9dbd
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -0,0 +1,1438 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drm.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_rect.h>
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/component.h>
+
+#include <linux/reset.h>
+#include <linux/iommu.h>
+#include <linux/delay.h>
+
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_fbdev.h"
+#include "rockchip_drm_gem.h"
+#include "rockchip_drm_fb.h"
+#include "rockchip_drm_vop.h"
+
+#define VOP_DEFAULT_FRAMERATE 60
+#define VOP_MAX_WIN_SUPPORT 5
+#define VOP_DEFAULT_PRIMARY 0
+#define VOP_DEFAULT_CURSOR 1
+#define VOP_REG(off, _mask, s) \
+ {.offset = off, \
+ .mask = _mask, \
+ .shift = s,}
+
+#define __REG_SET_RELAXED(x, off, mask, shift, v) \
+ vop_mask_write_relaxed(x, off, (mask) << shift, (v) << shift)
+#define __REG_SET_NORMAL(x, off, mask, shift, v) \
+ vop_mask_write(x, off, (mask) << shift, (v) << shift)
+
+#define REG_SET(x, base, reg, v, mode) \
+ __REG_SET_##mode(x, base + reg.offset, reg.mask, reg.shift, v)
+
+#define VOP_WIN_SET(x, win, name, v) \
+ REG_SET(x, win->base, win->phy->name, v, RELAXED)
+#define VOP_CTRL_SET(x, name, v) \
+ REG_SET(x, 0, (x)->data->ctrl->name, v, NORMAL)
+
+#define VOP_WIN_GET_YRGBADDR(ctx, win) \
+ vop_readl(ctx, win->base + win->phy->yrgb_mst.offset)
+
+#define to_vop_ctx(x) container_of(x, struct vop_context, crtc)
+#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
+
+struct rockchip_plane {
+ int id;
+ struct drm_plane base;
+ const struct vop_win *win;
+
+ uint32_t pending_yrgb_mst;
+ struct drm_framebuffer *front_fb;
+ struct drm_framebuffer *pending_fb;
+ bool enabled;
+};
+
+struct vop_context {
+ struct device *dev;
+ struct drm_device *drm_dev;
+ struct drm_crtc crtc;
+ struct drm_pending_vblank_event *event;
+ struct vop_driver *drv;
+ unsigned int dpms;
+
+ int connector_type;
+ int connector_out_mode;
+ wait_queue_head_t wait_vsync_queue;
+ atomic_t wait_vsync_event;
+
+ struct workqueue_struct *vsync_wq;
+ struct work_struct vsync_work;
+
+ /* mutex vsync_ work */
+ struct mutex vsync_mutex;
+ bool vsync_work_pending;
+
+ struct vop_driver_data *data;
+
+ uint32_t *regsbak;
+ void __iomem *regs;
+
+ /* physical map length of vop register */
+ uint32_t len;
+
+ /* one time only one process allowed to config the register */
+ spinlock_t reg_lock;
+ /* lock vop irq reg */
+ spinlock_t irq_lock;
+
+ unsigned int irq;
+
+ /* vop AHP clk */
+ struct clk *hclk;
+ /* vop dclk */
+ struct clk *dclk;
+ /* vop share memory frequency */
+ struct clk *aclk;
+
+ /* vop dclk reset */
+ struct reset_control *dclk_rst;
+
+ int pipe;
+ bool clk_on;
+ bool is_iommu_attach;
+};
+
+enum vop_data_format {
+ VOP_FMT_ARGB8888 = 0,
+ VOP_FMT_RGB888,
+ VOP_FMT_RGB565,
+ VOP_FMT_YUV420SP = 4,
+ VOP_FMT_YUV422SP,
+ VOP_FMT_YUV444SP,
+};
+
+struct vop_reg_data {
+ uint32_t offset;
+ uint32_t value;
+};
+
+struct vop_reg {
+ uint32_t offset;
+ uint32_t shift;
+ uint32_t mask;
+};
+
+struct vop_ctrl {
+ struct vop_reg standby;
+ struct vop_reg data_blank;
+ struct vop_reg gate_en;
+ struct vop_reg mmu_en;
+ struct vop_reg rgb_en;
+ struct vop_reg edp_en;
+ struct vop_reg hdmi_en;
+ struct vop_reg mipi_en;
+ struct vop_reg out_mode;
+ struct vop_reg dither_down;
+ struct vop_reg dither_up;
+ struct vop_reg pin_pol;
+
+ struct vop_reg htotal_pw;
+ struct vop_reg hact_st_end;
+ struct vop_reg vtotal_pw;
+ struct vop_reg vact_st_end;
+ struct vop_reg hpost_st_end;
+ struct vop_reg vpost_st_end;
+};
+
+struct vop_win_phy {
+ const uint32_t *data_formats;
+ uint32_t nformats;
+
+ struct vop_reg enable;
+ struct vop_reg format;
+ struct vop_reg act_info;
+ struct vop_reg dsp_info;
+ struct vop_reg dsp_st;
+ struct vop_reg yrgb_mst;
+ struct vop_reg uv_mst;
+ struct vop_reg yrgb_vir;
+ struct vop_reg uv_vir;
+
+ struct vop_reg dst_alpha_ctl;
+ struct vop_reg src_alpha_ctl;
+};
+
+struct vop_win {
+ uint32_t base;
+ const struct vop_win_phy *phy;
+};
+
+struct vop_driver_data {
+ const void *init_table;
+ int table_size;
+ const struct vop_ctrl *ctrl;
+ const struct vop_win *win[VOP_MAX_WIN_SUPPORT];
+};
+
+static const uint32_t formats_01[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_NV12,
+ DRM_FORMAT_NV16,
+ DRM_FORMAT_NV24,
+};
+
+static const uint32_t formats_234[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_RGB565,
+};
+
+static const struct vop_win_phy win01_data = {
+ .data_formats = formats_01,
+ .nformats = ARRAY_SIZE(formats_01),
+ .enable = VOP_REG(WIN0_CTRL0, 0x1, 0),
+ .format = VOP_REG(WIN0_CTRL0, 0x7, 1),
+ .act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0),
+ .dsp_info = VOP_REG(WIN0_DSP_INFO, 0x0fff0fff, 0),
+ .dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(WIN0_YRGB_MST, 0xffffffff, 0),
+ .uv_mst = VOP_REG(WIN0_CBR_MST, 0xffffffff, 0),
+ .yrgb_vir = VOP_REG(WIN0_VIR, 0x3fff, 0),
+ .uv_vir = VOP_REG(WIN0_VIR, 0x3fff, 16),
+ .src_alpha_ctl = VOP_REG(WIN0_SRC_ALPHA_CTRL, 0xff, 0),
+ .dst_alpha_ctl = VOP_REG(WIN0_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_phy win23_data = {
+ .data_formats = formats_234,
+ .nformats = ARRAY_SIZE(formats_234),
+ .enable = VOP_REG(WIN2_CTRL0, 0x1, 0),
+ .format = VOP_REG(WIN2_CTRL0, 0x7, 1),
+ .dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0),
+ .dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0),
+ .yrgb_vir = VOP_REG(WIN2_VIR0_1, 0x1fff, 0),
+ .src_alpha_ctl = VOP_REG(WIN2_SRC_ALPHA_CTRL, 0xff, 0),
+ .dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0),
+};
+
+static const struct vop_win_phy cursor_data = {
+ .data_formats = formats_234,
+ .nformats = ARRAY_SIZE(formats_234),
+ .enable = VOP_REG(HWC_CTRL0, 0x1, 0),
+ .format = VOP_REG(HWC_CTRL0, 0x7, 1),
+ .dsp_st = VOP_REG(HWC_DSP_ST, 0x1fff1fff, 0),
+ .yrgb_mst = VOP_REG(HWC_MST, 0xffffffff, 0),
+};
+
+static const struct vop_win win0 = {
+ .base = 0,
+ .phy = &win01_data,
+};
+
+static const struct vop_win win1 = {
+ .base = 0x40,
+ .phy = &win01_data,
+};
+
+static const struct vop_win win2 = {
+ .base = 0,
+ .phy = &win23_data,
+};
+
+static const struct vop_win win3 = {
+ .base = 0x50,
+ .phy = &win23_data,
+};
+
+static const struct vop_win win_cursor = {
+ .base = 0,
+ .phy = &cursor_data,
+};
+
+static const struct vop_ctrl ctrl_data = {
+ .standby = VOP_REG(SYS_CTRL, 0x1, 22),
+ .gate_en = VOP_REG(SYS_CTRL, 0x1, 23),
+ .mmu_en = VOP_REG(SYS_CTRL, 0x1, 20),
+ .rgb_en = VOP_REG(SYS_CTRL, 0x1, 12),
+ .hdmi_en = VOP_REG(SYS_CTRL, 0x1, 13),
+ .edp_en = VOP_REG(SYS_CTRL, 0x1, 14),
+ .mipi_en = VOP_REG(SYS_CTRL, 0x1, 15),
+ .dither_down = VOP_REG(DSP_CTRL1, 0xf, 1),
+ .dither_up = VOP_REG(DSP_CTRL1, 0x1, 6),
+ .data_blank = VOP_REG(DSP_CTRL0, 0x1, 19),
+ .out_mode = VOP_REG(DSP_CTRL0, 0xf, 0),
+ .pin_pol = VOP_REG(DSP_CTRL0, 0xf, 4),
+ .htotal_pw = VOP_REG(DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+ .hact_st_end = VOP_REG(DSP_HACT_ST_END, 0x1fff1fff, 0),
+ .vtotal_pw = VOP_REG(DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+ .vact_st_end = VOP_REG(DSP_VACT_ST_END, 0x1fff1fff, 0),
+ .hpost_st_end = VOP_REG(POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+ .vpost_st_end = VOP_REG(POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+};
+
+static const struct vop_reg_data vop_init_reg_table[] = {
+ {SYS_CTRL, 0x00c00000},
+ {DSP_CTRL0, 0x00000000},
+ {WIN0_CTRL0, 0x00000080},
+ {WIN1_CTRL0, 0x00000080},
+};
+
+static const struct vop_driver_data rockchip_rk3288_vop = {
+ .init_table = vop_init_reg_table,
+ .table_size = ARRAY_SIZE(vop_init_reg_table),
+ .ctrl = &ctrl_data,
+ .win[0] = &win0,
+ .win[1] = &win1,
+ .win[2] = &win2,
+ .win[3] = &win3,
+ .win[4] = &win_cursor,
+};
+
+static const struct of_device_id vop_driver_dt_match[] = {
+ { .compatible = "rockchip,rk3288-vop",
+ .data = (void *)&rockchip_rk3288_vop },
+ {},
+};
+
+static inline void vop_writel(struct vop_context *ctx,
+ uint32_t offset, uint32_t v)
+{
+ writel(v, ctx->regs + offset);
+ ctx->regsbak[offset >> 2] = v;
+}
+
+static inline uint32_t vop_readl(struct vop_context *ctx, uint32_t offset)
+{
+ return readl(ctx->regs + offset);
+}
+
+static inline void vop_cfg_done(struct vop_context *ctx)
+{
+ writel(0x01, ctx->regs + REG_CFG_DONE);
+}
+
+static inline void vop_mask_write(struct vop_context *ctx,
+ uint32_t offset, uint32_t mask, uint32_t v)
+{
+ if (mask) {
+ uint32_t cached_val = ctx->regsbak[offset >> 2];
+
+ cached_val = (cached_val & ~mask) | v;
+ writel(cached_val, ctx->regs + offset);
+ ctx->regsbak[offset >> 2] = cached_val;
+ }
+}
+
+static inline void vop_mask_write_relaxed(struct vop_context *ctx,
+ uint32_t offset, uint32_t mask,
+ uint32_t v)
+{
+ if (mask) {
+ uint32_t cached_val = ctx->regsbak[offset >> 2];
+
+ cached_val = (cached_val & ~mask) | v;
+ writel_relaxed(cached_val, ctx->regs + offset);
+ ctx->regsbak[offset >> 2] = cached_val;
+ }
+}
+
+static inline struct vop_driver_data *vop_get_driver_data(struct device *dev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(vop_driver_dt_match, dev);
+
+ return (struct vop_driver_data *)of_id->data;
+}
+
+static enum vop_data_format vop_convert_format(uint32_t format)
+{
+ switch (format) {
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB8888:
+ return VOP_FMT_ARGB8888;
+ case DRM_FORMAT_RGB888:
+ return VOP_FMT_RGB888;
+ case DRM_FORMAT_RGB565:
+ return VOP_FMT_RGB565;
+ case DRM_FORMAT_NV12:
+ return VOP_FMT_YUV420SP;
+ case DRM_FORMAT_NV16:
+ return VOP_FMT_YUV422SP;
+ case DRM_FORMAT_NV24:
+ return VOP_FMT_YUV444SP;
+ default:
+ DRM_ERROR("unsupport format[%08x]\n", format);
+ return -EINVAL;
+ }
+}
+
+static bool is_alpha_support(uint32_t format)
+{
+ switch (format) {
+ case DRM_FORMAT_ARGB8888:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* TODO(djkurtz): move generic 'setup slave rk_iommu' code somewhere common */
+static int vop_iommu_init(struct vop_context *ctx)
+{
+ struct device *dev = ctx->dev;
+ struct device_node *np = dev->of_node;
+ struct platform_device *pd;
+ int count;
+ int ret;
+ struct of_phandle_args args;
+
+ /* Each VOP must have exactly one iommu node, with no args */
+ count = of_count_phandle_with_args(np, "iommus", "#iommu-cells");
+ if (count != 1) {
+ dev_err(dev, "of_count_phandle_with_args(%s) => %d\n",
+ np->full_name, count);
+ return -EINVAL;
+ }
+
+ ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
+ &args);
+ if (ret) {
+ dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n",
+ np->full_name, ret);
+ return ret;
+ }
+ if (args.args_count != 0) {
+ dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n",
+ args.np->full_name, args.args_count);
+ return -EINVAL;
+ }
+
+ pd = of_find_device_by_node(args.np);
+ of_node_put(args.np);
+ if (!pd) {
+ dev_err(dev, "iommu %s not found\n", args.np->full_name);
+ return -EPROBE_DEFER;
+ }
+
+ /* TODO(djkurtz): handle multiple slave iommus for a single master */
+ dev->archdata.iommu = &pd->dev;
+
+ return 0;
+}
+
+static int vop_clk_enable(struct vop_context *ctx)
+{
+ int ret;
+
+ if (ctx->clk_on)
+ return 0;
+
+ ret = clk_enable(ctx->hclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to enable hclk - %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_enable(ctx->dclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to enable dclk - %d\n", ret);
+ goto err_disable_dclk;
+ }
+
+ ret = clk_enable(ctx->aclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to enable aclk - %d\n", ret);
+ goto err_disable_aclk;
+ }
+ ctx->clk_on = true;
+
+ return ret;
+err_disable_aclk:
+ clk_disable(ctx->aclk);
+err_disable_dclk:
+ clk_disable(ctx->dclk);
+ return ret;
+}
+
+static void vop_clk_disable(struct vop_context *ctx)
+{
+ if (!ctx->clk_on)
+ return;
+ clk_disable(ctx->aclk);
+ clk_disable(ctx->dclk);
+ clk_disable(ctx->hclk);
+ ctx->clk_on = false;
+}
+
+static void vop_power_on(struct vop_context *ctx)
+{
+ if (vop_clk_enable(ctx) < 0) {
+ dev_err(ctx->dev, "failed to enable clks\n");
+ return;
+ }
+
+ spin_lock(&ctx->reg_lock);
+
+ VOP_CTRL_SET(ctx, standby, 0);
+
+ spin_unlock(&ctx->reg_lock);
+}
+
+static void vop_power_off(struct vop_context *ctx)
+{
+ spin_lock(&ctx->reg_lock);
+
+ VOP_CTRL_SET(ctx, standby, 1);
+
+ spin_unlock(&ctx->reg_lock);
+
+ vop_clk_disable(ctx);
+}
+
+static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
+ struct drm_framebuffer *fb, int crtc_x,
+ int crtc_y, unsigned int crtc_w,
+ unsigned int crtc_h, uint32_t src_x,
+ uint32_t src_y, uint32_t src_w, uint32_t src_h)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ const struct vop_win *win = rockchip_plane->win;
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ struct drm_gem_object *obj;
+ struct rockchip_gem_object *rk_obj;
+ unsigned long offset;
+ unsigned int actual_w;
+ unsigned int actual_h;
+ unsigned int dsp_stx;
+ unsigned int dsp_sty;
+ unsigned int y_vir_stride;
+ dma_addr_t yrgb_mst;
+ enum vop_data_format format;
+ uint32_t val;
+ bool is_alpha;
+ bool visible;
+ int ret;
+ struct drm_rect dest = {
+ .x1 = crtc_x,
+ .y1 = crtc_y,
+ .x2 = crtc_x + crtc_w,
+ .y2 = crtc_y + crtc_h,
+ };
+ struct drm_rect src = {
+ /* 16.16 fixed point */
+ .x1 = src_x,
+ .y1 = src_y,
+ .x2 = src_x + src_w,
+ .y2 = src_y + src_h,
+ };
+ const struct drm_rect clip = {
+ .x2 = crtc->mode.hdisplay,
+ .y2 = crtc->mode.vdisplay,
+ };
+ bool can_position = plane->type != DRM_PLANE_TYPE_PRIMARY;
+
+ ret = drm_plane_helper_check_update(plane, crtc, fb,
+ &src, &dest, &clip,
+ DRM_PLANE_HELPER_NO_SCALING,
+ DRM_PLANE_HELPER_NO_SCALING,
+ can_position, false, &visible);
+ if (ret)
+ return ret;
+
+ if (!visible)
+ return 0;
+
+ is_alpha = is_alpha_support(fb->pixel_format);
+ format = vop_convert_format(fb->pixel_format);
+ if (format < 0)
+ return format;
+
+ obj = rockchip_fb_get_gem_obj(fb, 0);
+ if (!obj) {
+ DRM_ERROR("fail to get rockchip gem object from framebuffer\n");
+ return -EINVAL;
+ }
+
+ rk_obj = to_rockchip_obj(obj);
+
+ yrgb_mst = rk_obj->dma_addr;
+ actual_w = (src.x2 - src.x1) >> 16;
+ actual_h = (src.y2 - src.y1) >> 16;
+ crtc_x = max(0, crtc_x);
+ crtc_y = max(0, crtc_y);
+
+ dsp_stx = crtc_x + crtc->mode.htotal - crtc->mode.hsync_start;
+ dsp_sty = crtc_y + crtc->mode.vtotal - crtc->mode.vsync_start;
+
+ offset = (src.x1 >> 16) * (fb->bits_per_pixel >> 3);
+ offset += (src.y1 >> 16) * fb->pitches[0];
+
+ y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
+
+ spin_lock(&ctx->reg_lock);
+
+ VOP_WIN_SET(ctx, win, format, format);
+ VOP_WIN_SET(ctx, win, yrgb_vir, y_vir_stride);
+ yrgb_mst += offset;
+ VOP_WIN_SET(ctx, win, yrgb_mst, yrgb_mst);
+ val = (actual_h - 1) << 16;
+ val |= (actual_w - 1) & 0xffff;
+ VOP_WIN_SET(ctx, win, act_info, val);
+ VOP_WIN_SET(ctx, win, dsp_info, val);
+ val = (dsp_sty - 1) << 16;
+ val |= (dsp_stx - 1) & 0xffff;
+ VOP_WIN_SET(ctx, win, dsp_st, val);
+
+ if (is_alpha) {
+ VOP_WIN_SET(ctx, win, dst_alpha_ctl,
+ DST_FACTOR_M0(ALPHA_SRC_INVERSE));
+ val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
+ SRC_ALPHA_M0(ALPHA_STRAIGHT) |
+ SRC_BLEND_M0(ALPHA_PER_PIX) |
+ SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
+ SRC_FACTOR_M0(ALPHA_ONE);
+ VOP_WIN_SET(ctx, win, src_alpha_ctl, val);
+ } else {
+ VOP_WIN_SET(ctx, win, src_alpha_ctl, SRC_ALPHA_EN(0));
+ }
+
+ VOP_WIN_SET(ctx, win, enable, 1);
+
+ spin_unlock(&ctx->reg_lock);
+
+ mutex_lock(&ctx->vsync_mutex);
+
+ /*
+ * Because the buffer set to vop take effect at frame start time,
+ * we need make sure old buffer is not in use before we release
+ * it.
+ * reference the framebuffer, and unference it when it swap out of vop.
+ */
+ if (fb != rockchip_plane->front_fb) {
+ drm_framebuffer_reference(fb);
+ if (rockchip_plane->pending_fb)
+ drm_framebuffer_unreference(rockchip_plane->pending_fb);
+ rockchip_plane->pending_fb = fb;
+ rockchip_plane->pending_yrgb_mst = yrgb_mst;
+ ctx->vsync_work_pending = true;
+ }
+ rockchip_plane->enabled = true;
+
+ mutex_unlock(&ctx->vsync_mutex);
+
+ spin_lock(&ctx->reg_lock);
+ vop_cfg_done(ctx);
+ spin_unlock(&ctx->reg_lock);
+
+ return 0;
+}
+
+static inline int rockchip_update_primary_plane(struct drm_crtc *crtc)
+{
+ unsigned int crtc_w, crtc_h;
+
+ crtc_w = crtc->primary->fb->width - crtc->x;
+ crtc_h = crtc->primary->fb->height - crtc->y;
+
+ return rockchip_update_plane(crtc->primary, crtc, crtc->primary->fb,
+ 0, 0, crtc_w, crtc_h, crtc->x << 16,
+ crtc->y << 16, crtc_w << 16, crtc_h << 16);
+}
+
+static int rockchip_disable_plane(struct drm_plane *plane)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+ const struct vop_win *win = rockchip_plane->win;
+ struct vop_context *ctx;
+
+ if (!plane->crtc || !rockchip_plane->enabled)
+ return 0;
+
+ ctx = to_vop_ctx(plane->crtc);
+ spin_lock(&ctx->reg_lock);
+
+ VOP_WIN_SET(ctx, win, enable, 0);
+ vop_cfg_done(ctx);
+
+ spin_unlock(&ctx->reg_lock);
+
+ mutex_lock(&ctx->vsync_mutex);
+
+ /*
+ * clear the pending framebuffer and set vsync_work_pending true,
+ * so that the framebuffer will unref at the next vblank.
+ */
+ if (rockchip_plane->pending_fb) {
+ drm_framebuffer_unreference(rockchip_plane->pending_fb);
+ rockchip_plane->pending_fb = NULL;
+ }
+
+ rockchip_plane->enabled = false;
+ ctx->vsync_work_pending = true;
+
+ mutex_unlock(&ctx->vsync_mutex);
+
+ return 0;
+}
+
+static void rockchip_plane_destroy(struct drm_plane *plane)
+{
+ struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
+
+ rockchip_disable_plane(plane);
+ drm_plane_cleanup(plane);
+ kfree(rockchip_plane);
+}
+
+static const struct drm_plane_funcs rockchip_plane_funcs = {
+ .update_plane = rockchip_update_plane,
+ .disable_plane = rockchip_disable_plane,
+ .destroy = rockchip_plane_destroy,
+};
+
+static struct drm_plane *rockchip_plane_init(struct vop_context *ctx,
+ unsigned long possible_crtcs,
+ enum drm_plane_type type,
+ int index)
+{
+ struct rockchip_plane *rockchip_plane;
+ struct vop_driver_data *vop_data = ctx->data;
+ const struct vop_win *win;
+ int err;
+
+ if (index >= VOP_MAX_WIN_SUPPORT)
+ return ERR_PTR(-EINVAL);
+
+ rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
+ if (!rockchip_plane)
+ return ERR_PTR(-ENOMEM);
+
+ win = vop_data->win[index];
+ rockchip_plane->id = index;
+ rockchip_plane->win = win;
+
+ err = drm_universal_plane_init(ctx->drm_dev, &rockchip_plane->base,
+ possible_crtcs, &rockchip_plane_funcs,
+ win->phy->data_formats,
+ win->phy->nformats, type);
+ if (err) {
+ DRM_ERROR("failed to initialize plane\n");
+ kfree(rockchip_plane);
+ return ERR_PTR(err);
+ }
+
+ return &rockchip_plane->base;
+}
+
+int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc,
+ int connector_type,
+ int out_mode)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+
+ ctx->connector_type = connector_type;
+ ctx->connector_out_mode = out_mode;
+
+ return 0;
+}
+
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
+{
+ struct vop_context *ctx = to_vop_ctx(rockchip_drm_find_crtc(dev, pipe));
+ unsigned long flags;
+
+ if (ctx->dpms != DRM_MODE_DPMS_ON)
+ return -EPERM;
+
+ spin_lock_irqsave(&ctx->irq_lock, flags);
+
+ vop_mask_write(ctx, INTR_CTRL0, FS_INTR_MASK,
+ FS_INTR_EN(1));
+
+ spin_unlock_irqrestore(&ctx->irq_lock, flags);
+
+ return 0;
+}
+
+void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
+{
+ struct vop_context *ctx = to_vop_ctx(rockchip_drm_find_crtc(dev, pipe));
+ unsigned long flags;
+
+ if (ctx->dpms != DRM_MODE_DPMS_ON)
+ return;
+ spin_lock_irqsave(&ctx->irq_lock, flags);
+ vop_mask_write(ctx, INTR_CTRL0, FS_INTR_MASK,
+ FS_INTR_EN(0));
+ spin_unlock_irqrestore(&ctx->irq_lock, flags);
+}
+
+static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+
+ DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
+
+ if (ctx->dpms == mode) {
+ DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
+ return;
+ }
+ if (mode > DRM_MODE_DPMS_ON) {
+ struct drm_plane *plane;
+
+ drm_vblank_off(crtc->dev, ctx->pipe);
+ /*
+ * disable plane, then do dclk_reset to make disable take
+ * affect. so we can safely detach iommu.
+ */
+ list_for_each_entry(plane, &crtc->dev->mode_config.plane_list,
+ head) {
+ if (plane->crtc && (plane->crtc == crtc))
+ plane->funcs->disable_plane(plane);
+ }
+ reset_control_assert(ctx->dclk_rst);
+ usleep_range(10, 20);
+ reset_control_deassert(ctx->dclk_rst);
+
+ if (ctx->is_iommu_attach) {
+ rockchip_drm_dma_detach_device(ctx->drm_dev, ctx->dev);
+ ctx->is_iommu_attach = false;
+ }
+ }
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ vop_power_on(ctx);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ vop_power_off(ctx);
+ break;
+ default:
+ DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+ break;
+ }
+
+ ctx->dpms = mode;
+}
+
+static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+ rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
+ return false;
+
+ return true;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb);
+
+static int rockchip_drm_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *fb)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
+ u16 hdisplay = adjusted_mode->hdisplay;
+ u16 htotal = adjusted_mode->htotal;
+ u16 hact_st = adjusted_mode->htotal - adjusted_mode->hsync_start;
+ u16 hact_end = hact_st + hdisplay;
+ u16 vdisplay = adjusted_mode->vdisplay;
+ u16 vtotal = adjusted_mode->vtotal;
+ u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
+ u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
+ u16 vact_end = vact_st + vdisplay;
+ int ret;
+ uint32_t val;
+
+ /*
+ * disable dclk to stop frame scan, so that we can safe config mode and
+ * enable iommu.
+ */
+ clk_disable(ctx->dclk);
+
+ ret = rockchip_drm_crtc_mode_set_base(crtc, x, y, fb);
+ if (ret)
+ return ret;
+
+ switch (ctx->connector_type) {
+ case DRM_MODE_CONNECTOR_LVDS:
+ VOP_CTRL_SET(ctx, rgb_en, 1);
+ break;
+ case DRM_MODE_CONNECTOR_eDP:
+ VOP_CTRL_SET(ctx, edp_en, 1);
+ break;
+ case DRM_MODE_CONNECTOR_HDMIA:
+ VOP_CTRL_SET(ctx, hdmi_en, 1);
+ break;
+ default:
+ DRM_ERROR("unsupport connector_type[%d]\n",
+ ctx->connector_type);
+ return -EINVAL;
+ };
+ VOP_CTRL_SET(ctx, out_mode, ctx->connector_out_mode);
+
+ val = 0x8;
+ val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0;
+ val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? (1 << 1) : 0;
+ VOP_CTRL_SET(ctx, pin_pol, val);
+
+ VOP_CTRL_SET(ctx, htotal_pw, (htotal << 16) | hsync_len);
+ val = hact_st << 16;
+ val |= hact_end;
+ VOP_CTRL_SET(ctx, hact_st_end, val);
+ VOP_CTRL_SET(ctx, hpost_st_end, val);
+
+ VOP_CTRL_SET(ctx, vtotal_pw, (vtotal << 16) | vsync_len);
+ val = vact_st << 16;
+ val |= vact_end;
+ VOP_CTRL_SET(ctx, vact_st_end, val);
+ VOP_CTRL_SET(ctx, vpost_st_end, val);
+
+ /*
+ * do dclk_reset, let all config take affect, so new clk will safely
+ * run in correct frame.
+ */
+ reset_control_assert(ctx->dclk_rst);
+ usleep_range(10, 20);
+ reset_control_deassert(ctx->dclk_rst);
+
+ if (!ctx->is_iommu_attach) {
+ /*
+ * attach iommu device after dclk reset, to make sure vop scan
+ * the correct address.
+ */
+ ret = rockchip_drm_dma_attach_device(ctx->drm_dev, ctx->dev);
+ if (ret) {
+ dev_err(ctx->dev, "failed to attach dma mapping, %d\n",
+ ret);
+ return ret;
+ }
+ ctx->is_iommu_attach = true;
+ }
+
+ clk_set_rate(ctx->dclk, adjusted_mode->clock * 1000);
+ clk_enable(ctx->dclk);
+
+ return 0;
+}
+
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ int ret;
+
+ crtc->x = x;
+ crtc->y = y;
+
+ ret = rockchip_update_primary_plane(crtc);
+ if (ret < 0) {
+ DRM_ERROR("fail to update plane\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
+{
+ /* just do dummy now */
+}
+
+static const struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
+ .dpms = rockchip_drm_crtc_dpms,
+ .prepare = rockchip_drm_crtc_prepare,
+ .mode_fixup = rockchip_drm_crtc_mode_fixup,
+ .mode_set = rockchip_drm_crtc_mode_set,
+ .mode_set_base = rockchip_drm_crtc_mode_set_base,
+ .commit = rockchip_drm_crtc_commit,
+};
+
+static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event,
+ uint32_t page_flip_flags)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ struct drm_framebuffer *old_fb = crtc->primary->fb;
+ int ret;
+
+ /* when the page flip is requested, crtc's dpms should be on */
+ if (ctx->dpms > DRM_MODE_DPMS_ON) {
+ DRM_DEBUG("failed page flip request at dpms[%d].\n", ctx->dpms);
+ return 0;
+ }
+
+ ret = drm_vblank_get(dev, ctx->pipe);
+ if (ret) {
+ DRM_DEBUG("failed to acquire vblank counter\n");
+ return ret;
+ }
+
+ spin_lock_irq(&dev->event_lock);
+ if (ctx->event) {
+ spin_unlock_irq(&dev->event_lock);
+ DRM_ERROR("already pending flip!\n");
+ return -EBUSY;
+ }
+ ctx->event = event;
+ atomic_set(&ctx->wait_vsync_event, 1);
+ spin_unlock_irq(&dev->event_lock);
+
+ crtc->primary->fb = fb;
+
+ ret = rockchip_update_primary_plane(crtc);
+ if (ret) {
+ crtc->primary->fb = old_fb;
+
+ spin_lock_irq(&dev->event_lock);
+ drm_vblank_put(dev, ctx->pipe);
+ atomic_set(&ctx->wait_vsync_event, 0);
+ ctx->event = NULL;
+ spin_unlock_irq(&dev->event_lock);
+ }
+
+ return ret;
+}
+
+static void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
+{
+ struct rockchip_drm_private *dev_priv = dev->dev_private;
+ struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
+ struct vop_context *ctx;
+ unsigned long flags;
+
+ ctx = to_vop_ctx(drm_crtc);
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+
+ if (ctx->event) {
+ drm_send_vblank_event(dev, -1, ctx->event);
+ drm_vblank_put(dev, pipe);
+ atomic_set(&ctx->wait_vsync_event, 0);
+ wake_up(&ctx->wait_vsync_queue);
+ ctx->event = NULL;
+ }
+
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+ struct vop_context *ctx = to_vop_ctx(crtc);
+ struct rockchip_drm_private *private = crtc->dev->dev_private;
+
+ private->crtc[ctx->pipe] = NULL;
+ drm_crtc_cleanup(crtc);
+}
+
+static const struct drm_crtc_funcs rockchip_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .page_flip = rockchip_drm_crtc_page_flip,
+ .destroy = rockchip_drm_crtc_destroy,
+};
+
+static void rockchip_vsync_worker(struct work_struct *work)
+{
+ struct vop_context *ctx = container_of(work, struct vop_context,
+ vsync_work);
+ struct drm_device *drm = ctx->drm_dev;
+ struct rockchip_drm_private *dev_priv = drm->dev_private;
+ struct drm_crtc *crtc = dev_priv->crtc[ctx->pipe];
+ struct rockchip_plane *rockchip_plane;
+ struct drm_plane *plane;
+ uint32_t yrgb_mst;
+
+ mutex_lock(&ctx->vsync_mutex);
+
+ ctx->vsync_work_pending = false;
+
+ list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
+ rockchip_plane = to_rockchip_plane(plane);
+
+ if (to_vop_ctx(plane->crtc) != ctx)
+ continue;
+ if (rockchip_plane->enabled && !rockchip_plane->pending_fb)
+ continue;
+ if (!rockchip_plane->enabled && !rockchip_plane->front_fb)
+ continue;
+ /*
+ * make sure the yrgb_mst take effect, so that
+ * we can unreference the old framebuffer.
+ */
+ yrgb_mst = VOP_WIN_GET_YRGBADDR(ctx, rockchip_plane->win);
+ if (rockchip_plane->pending_yrgb_mst != yrgb_mst) {
+ /*
+ * some plane no complete, unref at next vblank
+ */
+ ctx->vsync_work_pending = true;
+ continue;
+ }
+
+ /*
+ * drm_framebuffer_unreference maybe call iommu unmap,
+ * and iommu not allow unmap buffer at irq context,
+ * so we do drm_framebuffer_unreference at queue_work.
+ */
+ if (rockchip_plane->front_fb)
+ drm_framebuffer_unreference(rockchip_plane->front_fb);
+
+ rockchip_plane->front_fb = rockchip_plane->pending_fb;
+ rockchip_plane->pending_fb = NULL;
+
+ /*
+ * if primary plane flip complete, sending the event to
+ * userspace
+ */
+ if (&rockchip_plane->base == crtc->primary)
+ rockchip_drm_crtc_finish_pageflip(ctx->drm_dev,
+ ctx->pipe);
+ }
+
+ mutex_unlock(&ctx->vsync_mutex);
+}
+
+static irqreturn_t rockchip_vop_isr(int irq, void *data)
+{
+ struct vop_context *ctx = data;
+ uint32_t intr0_reg;
+ unsigned long flags;
+
+ intr0_reg = vop_readl(ctx, INTR_CTRL0);
+ if (intr0_reg & FS_INTR) {
+ spin_lock_irqsave(&ctx->irq_lock, flags);
+ vop_writel(ctx, INTR_CTRL0, intr0_reg | FS_INTR_CLR);
+ spin_unlock_irqrestore(&ctx->irq_lock, flags);
+ } else {
+ return IRQ_NONE;
+ }
+
+ drm_handle_vblank(ctx->drm_dev, ctx->pipe);
+ if (ctx->vsync_work_pending)
+ queue_work(ctx->vsync_wq, &ctx->vsync_work);
+
+ return IRQ_HANDLED;
+}
+
+static int vop_create_crtc(struct vop_context *ctx)
+{
+ struct device *dev = ctx->dev;
+ struct drm_device *drm_dev = ctx->drm_dev;
+ struct drm_plane *primary, *cursor;
+ unsigned long possible_crtcs;
+ struct drm_crtc *crtc;
+ int ret;
+ int nr;
+
+ crtc = &ctx->crtc;
+
+ ret = rockchip_drm_add_crtc(drm_dev, crtc, dev->of_node);
+ if (ret < 0)
+ return ret;
+ ctx->pipe = ret;
+
+ possible_crtcs = (1 << ctx->pipe);
+
+ for (nr = 0; nr < VOP_MAX_WIN_SUPPORT; nr++) {
+ if (nr == VOP_DEFAULT_PRIMARY) {
+ primary = rockchip_plane_init(ctx, possible_crtcs,
+ DRM_PLANE_TYPE_PRIMARY,
+ VOP_DEFAULT_PRIMARY);
+ if (IS_ERR(primary)) {
+ DRM_ERROR("fail to init primary plane\n");
+ return PTR_ERR(primary);
+ }
+ } else if (nr == VOP_DEFAULT_CURSOR) {
+ cursor = rockchip_plane_init(ctx, possible_crtcs,
+ DRM_PLANE_TYPE_CURSOR,
+ VOP_DEFAULT_CURSOR);
+ if (IS_ERR(cursor)) {
+ DRM_ERROR("fail to init cursor plane\n");
+ return PTR_ERR(cursor);
+ }
+ } else {
+ struct drm_plane *plane;
+
+ plane = rockchip_plane_init(ctx, possible_crtcs,
+ DRM_PLANE_TYPE_OVERLAY,
+ nr);
+ if (IS_ERR(plane)) {
+ DRM_ERROR("fail to init overlay plane\n");
+ return PTR_ERR(plane);
+ }
+ }
+ }
+
+ drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
+ &rockchip_crtc_funcs);
+ drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
+
+ return 0;
+}
+
+static int rockchip_vop_initial(struct vop_context *ctx)
+{
+ struct vop_driver_data *vop_data = ctx->data;
+ const struct vop_reg_data *init_table = vop_data->init_table;
+ struct reset_control *ahb_rst;
+ int i, ret;
+
+ ctx->hclk = devm_clk_get(ctx->dev, "hclk_vop");
+ if (IS_ERR(ctx->hclk)) {
+ dev_err(ctx->dev, "failed to get hclk source\n");
+ return PTR_ERR(ctx->hclk);
+ }
+ ctx->aclk = devm_clk_get(ctx->dev, "aclk_vop");
+ if (IS_ERR(ctx->aclk)) {
+ dev_err(ctx->dev, "failed to get aclk source\n");
+ return PTR_ERR(ctx->aclk);
+ }
+ ctx->dclk = devm_clk_get(ctx->dev, "dclk_vop");
+ if (IS_ERR(ctx->dclk)) {
+ dev_err(ctx->dev, "failed to get dclk source\n");
+ return PTR_ERR(ctx->dclk);
+ }
+
+ ret = clk_prepare(ctx->hclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to prepare hclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare(ctx->dclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to prepare dclk\n");
+ goto err_unprepare_hclk;
+ }
+
+ ret = clk_prepare(ctx->aclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to prepare aclk\n");
+ goto err_unprepare_dclk;
+ }
+
+ ctx->dclk_rst = devm_reset_control_get(ctx->dev, "dclk");
+ if (IS_ERR(ctx->dclk_rst)) {
+ dev_err(ctx->dev, "failed to get dclk reset\n");
+ ret = PTR_ERR(ctx->dclk_rst);
+ goto err_unprepare_aclk;
+ }
+
+ /*
+ * enable hclk, so that we can config vop register.
+ */
+ ret = clk_enable(ctx->hclk);
+ if (ret < 0) {
+ dev_err(ctx->dev, "failed to prepare aclk\n");
+ goto err_unprepare_aclk;
+ }
+ /*
+ * do hclk_reset, reset all vop registers.
+ */
+ ahb_rst = devm_reset_control_get(ctx->dev, "ahb");
+ if (IS_ERR(ahb_rst)) {
+ dev_err(ctx->dev, "failed to get ahb reset\n");
+ ret = PTR_ERR(ahb_rst);
+ goto err_disable_hclk;
+ }
+ reset_control_assert(ahb_rst);
+ usleep_range(10, 20);
+ reset_control_deassert(ahb_rst);
+
+ memcpy(ctx->regsbak, ctx->regs, ctx->len);
+
+ for (i = 0; i < vop_data->table_size; i++)
+ vop_writel(ctx, init_table[i].offset, init_table[i].value);
+
+ for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++)
+ VOP_WIN_SET(ctx, vop_data->win[i], enable, 0);
+
+ clk_disable(ctx->hclk);
+
+ ctx->dpms = DRM_MODE_DPMS_OFF;
+
+ return 0;
+
+err_disable_hclk:
+ clk_disable(ctx->hclk);
+err_unprepare_aclk:
+ clk_unprepare(ctx->aclk);
+err_unprepare_dclk:
+ clk_unprepare(ctx->dclk);
+err_unprepare_hclk:
+ clk_unprepare(ctx->hclk);
+ return ret;
+}
+
+static int vop_bind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vop_driver_data *vop_data = vop_get_driver_data(dev);
+ struct drm_device *drm_dev = data;
+ struct vop_context *ctx;
+ struct resource *res;
+ int ret;
+
+ if (!vop_data)
+ return -ENODEV;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = dev;
+ ctx->data = vop_data;
+ ctx->drm_dev = drm_dev;
+ dev_set_drvdata(dev, ctx);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctx->len = resource_size(res);
+ ctx->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctx->regs))
+ return PTR_ERR(ctx->regs);
+
+ ctx->regsbak = devm_kzalloc(dev, ctx->len, GFP_KERNEL);
+ if (!ctx->regsbak)
+ return -ENOMEM;
+
+ ret = rockchip_vop_initial(ctx);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
+ return ret;
+ }
+
+ ctx->irq = platform_get_irq(pdev, 0);
+ if (ctx->irq < 0) {
+ dev_err(dev, "cannot find irq for vop\n");
+ return ctx->irq;
+ }
+
+ spin_lock_init(&ctx->reg_lock);
+ spin_lock_init(&ctx->irq_lock);
+
+ init_waitqueue_head(&ctx->wait_vsync_queue);
+ atomic_set(&ctx->wait_vsync_event, 0);
+
+ ret = vop_iommu_init(ctx);
+ if (ret) {
+ DRM_ERROR("Failed to setup iommu, %d\n", ret);
+ return ret;
+ }
+
+ ctx->vsync_wq = create_singlethread_workqueue("vsync");
+ if (!ctx->vsync_wq) {
+ dev_err(dev, "failed to create workqueue\n");
+ return -EINVAL;
+ }
+ INIT_WORK(&ctx->vsync_work, rockchip_vsync_worker);
+
+ mutex_init(&ctx->vsync_mutex);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_request_irq(dev, ctx->irq, rockchip_vop_isr,
+ IRQF_SHARED, dev_name(dev), ctx);
+ if (ret) {
+ dev_err(dev, "cannot requeset irq%d - err %d\n", ctx->irq, ret);
+ return ret;
+ }
+
+ return vop_create_crtc(ctx);
+}
+
+static void vop_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct drm_device *drm_dev = data;
+ struct vop_context *ctx = dev_get_drvdata(dev);
+ struct drm_crtc *crtc = &ctx->crtc;
+
+ drm_crtc_cleanup(crtc);
+ pm_runtime_disable(dev);
+ rockchip_drm_remove_crtc(drm_dev, ctx->pipe);
+}
+
+static const struct component_ops vop_component_ops = {
+ .bind = vop_bind,
+ .unbind = vop_unbind,
+};
+
+static int vop_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vop_context *ctx;
+
+ if (!dev->of_node) {
+ dev_err(dev, "can't find vop devices\n");
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, ctx);
+
+ return component_add(dev, &vop_component_ops);
+}
+
+static int vop_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &vop_component_ops);
+
+ return 0;
+}
+
+struct platform_driver rockchip_vop_platform_driver = {
+ .probe = vop_probe,
+ .remove = vop_remove,
+ .driver = {
+ .name = "rockchip-vop",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(vop_driver_dt_match),
+ },
+};
+
+module_platform_driver(rockchip_vop_platform_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP VOP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
new file mode 100644
index 0000000..952317c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_DRM_VOP_H
+#define _ROCKCHIP_DRM_VOP_H
+
+/* register definition */
+#define REG_CFG_DONE 0x0000
+#define VERSION_INFO 0x0004
+#define SYS_CTRL 0x0008
+#define SYS_CTRL1 0x000c
+#define DSP_CTRL0 0x0010
+#define DSP_CTRL1 0x0014
+#define DSP_BG 0x0018
+#define MCU_CTRL 0x001c
+#define INTR_CTRL0 0x0020
+#define INTR_CTRL1 0x0024
+#define WIN0_CTRL0 0x0030
+#define WIN0_CTRL1 0x0034
+#define WIN0_COLOR_KEY 0x0038
+#define WIN0_VIR 0x003c
+#define WIN0_YRGB_MST 0x0040
+#define WIN0_CBR_MST 0x0044
+#define WIN0_ACT_INFO 0x0048
+#define WIN0_DSP_INFO 0x004c
+#define WIN0_DSP_ST 0x0050
+#define WIN0_SCL_FACTOR_YRGB 0x0054
+#define WIN0_SCL_FACTOR_CBR 0x0058
+#define WIN0_SCL_OFFSET 0x005c
+#define WIN0_SRC_ALPHA_CTRL 0x0060
+#define WIN0_DST_ALPHA_CTRL 0x0064
+#define WIN0_FADING_CTRL 0x0068
+/* win1 register */
+#define WIN1_CTRL0 0x0070
+#define WIN1_CTRL1 0x0074
+#define WIN1_COLOR_KEY 0x0078
+#define WIN1_VIR 0x007c
+#define WIN1_YRGB_MST 0x0080
+#define WIN1_CBR_MST 0x0084
+#define WIN1_ACT_INFO 0x0088
+#define WIN1_DSP_INFO 0x008c
+#define WIN1_DSP_ST 0x0090
+#define WIN1_SCL_FACTOR_YRGB 0x0094
+#define WIN1_SCL_FACTOR_CBR 0x0098
+#define WIN1_SCL_OFFSET 0x009c
+#define WIN1_SRC_ALPHA_CTRL 0x00a0
+#define WIN1_DST_ALPHA_CTRL 0x00a4
+#define WIN1_FADING_CTRL 0x00a8
+/* win2 register */
+#define WIN2_CTRL0 0x00b0
+#define WIN2_CTRL1 0x00b4
+#define WIN2_VIR0_1 0x00b8
+#define WIN2_VIR2_3 0x00bc
+#define WIN2_MST0 0x00c0
+#define WIN2_DSP_INFO0 0x00c4
+#define WIN2_DSP_ST0 0x00c8
+#define WIN2_COLOR_KEY 0x00cc
+#define WIN2_MST1 0x00d0
+#define WIN2_DSP_INFO1 0x00d4
+#define WIN2_DSP_ST1 0x00d8
+#define WIN2_SRC_ALPHA_CTRL 0x00dc
+#define WIN2_MST2 0x00e0
+#define WIN2_DSP_INFO2 0x00e4
+#define WIN2_DSP_ST2 0x00e8
+#define WIN2_DST_ALPHA_CTRL 0x00ec
+#define WIN2_MST3 0x00f0
+#define WIN2_DSP_INFO3 0x00f4
+#define WIN2_DSP_ST3 0x00f8
+#define WIN2_FADING_CTRL 0x00fc
+/* win3 register */
+#define WIN3_CTRL0 0x0100
+#define WIN3_CTRL1 0x0104
+#define WIN3_VIR0_1 0x0108
+#define WIN3_VIR2_3 0x010c
+#define WIN3_MST0 0x0110
+#define WIN3_DSP_INFO0 0x0114
+#define WIN3_DSP_ST0 0x0118
+#define WIN3_COLOR_KEY 0x011c
+#define WIN3_MST1 0x0120
+#define WIN3_DSP_INFO1 0x0124
+#define WIN3_DSP_ST1 0x0128
+#define WIN3_SRC_ALPHA_CTRL 0x012c
+#define WIN3_MST2 0x0130
+#define WIN3_DSP_INFO2 0x0134
+#define WIN3_DSP_ST2 0x0138
+#define WIN3_DST_ALPHA_CTRL 0x013c
+#define WIN3_MST3 0x0140
+#define WIN3_DSP_INFO3 0x0144
+#define WIN3_DSP_ST3 0x0148
+#define WIN3_FADING_CTRL 0x014c
+/* hwc register */
+#define HWC_CTRL0 0x0150
+#define HWC_CTRL1 0x0154
+#define HWC_MST 0x0158
+#define HWC_DSP_ST 0x015c
+#define HWC_SRC_ALPHA_CTRL 0x0160
+#define HWC_DST_ALPHA_CTRL 0x0164
+#define HWC_FADING_CTRL 0x0168
+/* post process register */
+#define POST_DSP_HACT_INFO 0x0170
+#define POST_DSP_VACT_INFO 0x0174
+#define POST_SCL_FACTOR_YRGB 0x0178
+#define POST_SCL_CTRL 0x0180
+#define POST_DSP_VACT_INFO_F1 0x0184
+#define DSP_HTOTAL_HS_END 0x0188
+#define DSP_HACT_ST_END 0x018c
+#define DSP_VTOTAL_VS_END 0x0190
+#define DSP_VACT_ST_END 0x0194
+#define DSP_VS_ST_END_F1 0x0198
+#define DSP_VACT_ST_END_F1 0x019c
+/* register definition end */
+
+/* interrupt define */
+#define DSP_HOLD_VALID_INTR (1 << 0)
+#define FS_INTR (1 << 1)
+#define LINE_FLAG_INTR (1 << 2)
+#define BUS_ERROR_INTR (1 << 3)
+
+#define DSP_HOLD_VALID_INTR_EN(x) ((x) << 4)
+#define FS_INTR_EN(x) ((x) << 5)
+#define LINE_FLAG_INTR_EN(x) ((x) << 6)
+#define BUS_ERROR_INTR_EN(x) ((x) << 7)
+#define DSP_HOLD_VALID_INTR_MASK (1 << 4)
+#define FS_INTR_MASK (1 << 5)
+#define LINE_FLAG_INTR_MASK (1 << 6)
+#define BUS_ERROR_INTR_MASK (1 << 7)
+
+#define DSP_HOLD_VALID_INTR_CLR (1 << 8)
+#define FS_INTR_CLR (1 << 9)
+#define LINE_FLAG_INTR_CLR (1 << 10)
+#define BUS_ERROR_INTR_CLR (1 << 11)
+#define DSP_LINE_NUM(x) (((x) & 0x1fff) << 12)
+#define DSP_LINE_NUM_MASK (0x1fff << 12)
+
+/* src alpha ctrl define */
+#define SRC_FADING_VALUE(x) (((x) & 0xff) << 24)
+#define SRC_GLOBAL_ALPHA(x) (((x) & 0xff) << 16)
+#define SRC_FACTOR_M0(x) (((x) & 0x7) << 6)
+#define SRC_ALPHA_CAL_M0(x) (((x) & 0x1) << 5)
+#define SRC_BLEND_M0(x) (((x) & 0x3) << 3)
+#define SRC_ALPHA_M0(x) (((x) & 0x1) << 2)
+#define SRC_COLOR_M0(x) (((x) & 0x1) << 1)
+#define SRC_ALPHA_EN(x) (((x) & 0x1) << 0)
+/* dst alpha ctrl define */
+#define DST_FACTOR_M0(x) (((x) & 0x7) << 6)
+
+/*
+ * display output interface supported by rockchip lcdc
+ */
+#define ROCKCHIP_OUT_MODE_P888 0
+#define ROCKCHIP_OUT_MODE_P666 1
+#define ROCKCHIP_OUT_MODE_P565 2
+/* for use special outface */
+#define ROCKCHIP_OUT_MODE_AAAA 15
+
+enum alpha_mode {
+ ALPHA_STRAIGHT,
+ ALPHA_INVERSE,
+};
+
+enum global_blend_mode {
+ ALPHA_GLOBAL,
+ ALPHA_PER_PIX,
+ ALPHA_PER_PIX_GLOBAL,
+};
+
+enum alpha_cal_mode {
+ ALPHA_SATURATION,
+ ALPHA_NO_SATURATION,
+};
+
+enum color_mode {
+ ALPHA_SRC_PRE_MUL,
+ ALPHA_SRC_NO_PRE_MUL,
+};
+
+enum factor_mode {
+ ALPHA_ZERO,
+ ALPHA_ONE,
+ ALPHA_SRC,
+ ALPHA_SRC_INVERSE,
+ ALPHA_SRC_GLOBAL,
+};
+
+#endif /* _ROCKCHIP_DRM_VOP_H */
--
1.7.9.5
^ permalink raw reply related
* [PATCH v8 0/3] Add drm driver for Rockchip Socs
From: Mark Yao @ 2014-09-30 6:09 UTC (permalink / raw)
To: heiko, 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, linux-doc, linux-kernel, dri-devel, linux-api,
linux-rockchip, dianders, marcheu, dbehr, olof, djkurtz, xjq, kfx,
cym, cf, zyw, xxm, huangtao, kever.yang, yxj, wxt, xw, Mark Yao
This a series of patches is a DRM Driver for Rockchip Socs, add support
for vop devices. Future patches will add additional encoders/connectors,
such as eDP, HDMI.
The basic "crtc" for rockchip is a "VOP" - Video Output Processor.
the vop devices found on Rockchip rk3288 Soc, rk3288 soc have two similar
Vop devices. Vop devices support iommu mapping, we use dma-mapping API with
ARM_DMA_USE_IOMMU.
Changes in v2:
- add DRM master device node to list all display nodes that comprise
the graphics subsystem.
- 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.
- add vop reset.
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 style 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, instead 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 ling 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
Mark yao (3):
drm: rockchip: Add basic drm driver
dt-bindings: video: Add for rockchip display subsytem
dt-bindings: video: Add documentation for rockchip vop
.../devicetree/bindings/video/rockchip-drm.txt | 19 +
.../devicetree/bindings/video/rockchip-vop.txt | 58 +
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 | 65 +
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 | 209 +++
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 | 1438 ++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 196 +++
16 files changed, 3118 insertions(+)
--
1.7.9.5
^ permalink raw reply
* Re: [PATCH v3] mm: add mremap flag for preserving the old mapping
From: Andy Lutomirski @ 2014-09-30 5:53 UTC (permalink / raw)
To: Daniel Micay
Cc: linux-mm-Bw31MaZKKs3YtjvyW6yDsg@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Linux API,
Andrew Morton, jasone-Z38pQHDqNGNl57MIdRCFDg
In-Reply-To: <1412052900-1722-1-git-send-email-danielmicay-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Mon, Sep 29, 2014 at 9:55 PM, Daniel Micay <danielmicay-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> This introduces the MREMAP_RETAIN flag for preserving the source mapping
> when MREMAP_MAYMOVE moves the pages to a new destination. Accesses to
> the source location will fault and cause fresh pages to be mapped in.
>
> For consistency, the old_len >= new_len case could decommit the pages
> instead of unmapping. However, userspace can accomplish the same thing
> via madvise and a coherent definition of the flag is possible without
> the extra complexity.
IMO this needs very clear documentation of exactly what it does.
Does it preserve the contents of the source pages? (If so, why?
Aren't you wasting a bunch of time on page faults and possibly
unnecessary COWs?)
Does it work on file mappings? Can it extend file mappings while it moves them?
If you MREMAP_RETAIN a partially COWed private mapping, what happens?
Does it work on special mappings? If so, please prevent it from doing
so. mremapping x86's vdso is a thing, and duplicating x86's vdso
should not become a thing, because x86_32 in particular will become
extremely confused.
--Andy
^ permalink raw reply
* [PATCH v3] mm: add mremap flag for preserving the old mapping
From: Daniel Micay @ 2014-09-30 4:55 UTC (permalink / raw)
To: linux-mm; +Cc: linux-kernel, linux-api, akpm, jasone, Daniel Micay
This introduces the MREMAP_RETAIN flag for preserving the source mapping
when MREMAP_MAYMOVE moves the pages to a new destination. Accesses to
the source location will fault and cause fresh pages to be mapped in.
For consistency, the old_len >= new_len case could decommit the pages
instead of unmapping. However, userspace can accomplish the same thing
via madvise and a coherent definition of the flag is possible without
the extra complexity.
Motivation:
TCMalloc and jemalloc avoid releasing virtual memory in order to reduce
virtual memory fragmentation. A call to munmap or mremap would leave a
hole in the address space. Instead, unused pages are lazily returned to
the operating system via MADV_DONTNEED.
Since mremap cannot be used to elide copies, TCMalloc and jemalloc end
up being significantly slower for patterns like repeated vector / hash
table reallocations. Consider the typical vector building pattern:
#include <string.h>
#include <stdlib.h>
int main(void) {
void *ptr = NULL;
size_t old_size = 0;
for (size_t size = 4; size < (1 << 30); size *= 2) {
ptr = realloc(ptr, size);
if (!ptr) return 1;
memset(ptr + old_size, 0xff, size - old_size);
old_size = size;
}
}
glibc (baseline, uses mremap already): 0.135s
jemalloc without MREMAP_RETAIN: 0.226s
jemalloc with MREMAP_RETAIN: 0.112s
TCMalloc without MREMAP_RETAIN: 0.238s
(the improvement should be similar to jemalloc)
In practice, in-place growth never occurs because the heap grows in the
downwards direction for all 3 allocators. TCMalloc and jemalloc pay for
enormous copies while glibc is only spending time writing new elements
to the vector. Even if it was grown in the other direction, real-world
applications would end up blocking in-place growth with new allocations.
The allocators could attempt to map the source location again after an
mremap call, but there is no guarantee of success in a multi-threaded
program and fragmentating memory over time is considered unacceptable.
Signed-off-by: Daniel Micay <danielmicay@gmail.com>
---
include/uapi/linux/mman.h | 1 +
mm/mremap.c | 39 ++++++++++++++++++++++++---------------
2 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/include/uapi/linux/mman.h b/include/uapi/linux/mman.h
index ade4acd..4e9a546 100644
--- a/include/uapi/linux/mman.h
+++ b/include/uapi/linux/mman.h
@@ -5,6 +5,7 @@
#define MREMAP_MAYMOVE 1
#define MREMAP_FIXED 2
+#define MREMAP_RETAIN 4
#define OVERCOMMIT_GUESS 0
#define OVERCOMMIT_ALWAYS 1
diff --git a/mm/mremap.c b/mm/mremap.c
index 05f1180..079334a 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -235,7 +235,8 @@ unsigned long move_page_tables(struct vm_area_struct *vma,
static unsigned long move_vma(struct vm_area_struct *vma,
unsigned long old_addr, unsigned long old_len,
- unsigned long new_len, unsigned long new_addr, bool *locked)
+ unsigned long new_len, unsigned long new_addr, bool retain,
+ bool *locked)
{
struct mm_struct *mm = vma->vm_mm;
struct vm_area_struct *new_vma;
@@ -287,15 +288,7 @@ static unsigned long move_vma(struct vm_area_struct *vma,
old_len = new_len;
old_addr = new_addr;
new_addr = -ENOMEM;
- }
-
- /* Conceal VM_ACCOUNT so old reservation is not undone */
- if (vm_flags & VM_ACCOUNT) {
- vma->vm_flags &= ~VM_ACCOUNT;
- excess = vma->vm_end - vma->vm_start - old_len;
- if (old_addr > vma->vm_start &&
- old_addr + old_len < vma->vm_end)
- split = 1;
+ retain = false;
}
/*
@@ -310,6 +303,19 @@ static unsigned long move_vma(struct vm_area_struct *vma,
hiwater_vm = mm->hiwater_vm;
vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT);
+ /* Leave the old mapping in place for MREMAP_RETAIN. */
+ if (retain)
+ goto out;
+
+ /* Conceal VM_ACCOUNT so old reservation is not undone */
+ if (vm_flags & VM_ACCOUNT) {
+ vma->vm_flags &= ~VM_ACCOUNT;
+ excess = vma->vm_end - vma->vm_start - old_len;
+ if (old_addr > vma->vm_start &&
+ old_addr + old_len < vma->vm_end)
+ split = 1;
+ }
+
if (do_munmap(mm, old_addr, old_len) < 0) {
/* OOM: unable to split vma, just get accounts right */
vm_unacct_memory(excess >> PAGE_SHIFT);
@@ -324,6 +330,7 @@ static unsigned long move_vma(struct vm_area_struct *vma,
vma->vm_next->vm_flags |= VM_ACCOUNT;
}
+out:
if (vm_flags & VM_LOCKED) {
mm->locked_vm += new_len >> PAGE_SHIFT;
*locked = true;
@@ -392,7 +399,8 @@ Eagain:
}
static unsigned long mremap_to(unsigned long addr, unsigned long old_len,
- unsigned long new_addr, unsigned long new_len, bool *locked)
+ unsigned long new_addr, unsigned long new_len, bool retain,
+ bool *locked)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
@@ -442,7 +450,7 @@ static unsigned long mremap_to(unsigned long addr, unsigned long old_len,
if (ret & ~PAGE_MASK)
goto out1;
- ret = move_vma(vma, addr, old_len, new_len, new_addr, locked);
+ ret = move_vma(vma, addr, old_len, new_len, new_addr, retain, locked);
if (!(ret & ~PAGE_MASK))
goto out;
out1:
@@ -482,7 +490,7 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
unsigned long charged = 0;
bool locked = false;
- if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE))
+ if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_RETAIN))
return ret;
if (flags & MREMAP_FIXED && !(flags & MREMAP_MAYMOVE))
@@ -506,7 +514,7 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
if (flags & MREMAP_FIXED) {
ret = mremap_to(addr, old_len, new_addr, new_len,
- &locked);
+ flags & MREMAP_RETAIN, &locked);
goto out;
}
@@ -575,7 +583,8 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
goto out;
}
- ret = move_vma(vma, addr, old_len, new_len, new_addr, &locked);
+ ret = move_vma(vma, addr, old_len, new_len, new_addr,
+ flags & MREMAP_RETAIN, &locked);
}
out:
if (ret & ~PAGE_MASK)
--
2.1.1
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related
* Re: [RESENT PATCH v7 1/3] drm: rockchip: Add basic drm driver
From: Mark yao @ 2014-09-30 0:35 UTC (permalink / raw)
To: Daniel Kurtz
Cc: Mark Rutland, Heiko Stübner, linux-doc, Kever Yang,
dri-devel, linux-kernel@vger.kernel.org, Jianqun Xu, Chris Zhong,
linux-api, jeff chen, linux-rockchip, kfx, Grant Likely,
王晓腾, Tao Huang, devicetree, Pawel Moll,
Ian Campbell, yxj, Eddie Cai, Rob Herring,
Stéphane Marchesin, simon xue, xw
In-Reply-To: <CAGS+omDbnoJsKmFdaMPJaNUx6Po5AMLLRJCENHHUGGHH=sqmug@mail.gmail.com>
[-- Attachment #1.1: Type: text/plain, Size: 126970 bytes --]
On 2014年09月29日 12:01, Daniel Kurtz wrote:
> Hi Mark,
>
> More review comments inline... (sorry for the delay)
Thanks for your review.
>
> On Fri, Sep 26, 2014 at 6:55 PM, Mark Yao <mark.yao@rock-chips.com> wrote:
>> From: Mark yao <mark.yao@rock-chips.com>
>>
>> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
>>
>> Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
>> Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
>> Acked-by: Daniel Vetter <daniel@ffwll.ch>
>> Reviewed-by: Rob Clark <robdclark@gmail.com>
>> ---
>> 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 style 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, instead of
>> using drm_diaplay_mode private method.
>> - change vop mode_set timing to make it more safely.
>>
>> Changes in v7:
>> - fix memory leakage problem
>>
>> 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 | 516 +++++++++
>> drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 61 ++
>> 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 | 345 ++++++
>> drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 55 +
>> drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1422 +++++++++++++++++++++++++
>> drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 196 ++++
>> 14 files changed, 3102 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)+= vmwgfx/
>> obj-$(CONFIG_DRM_VIA) +=via/
>> obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
>> obj-$(CONFIG_DRM_EXYNOS) +=exynos/
>> +obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
>> obj-$(CONFIG_DRM_GMA500) += gma500/
>> obj-$(CONFIG_DRM_UDL) += udl/
>> obj-$(CONFIG_DRM_AST) += ast/
>> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
>> new file mode 100644
>> index 0000000..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/rockchip/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 := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
>> + rockchip_drm_gem.o rockchip_drm_vop.o
>> +
>> +obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> new file mode 100644
>> index 0000000..a571acc
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> @@ -0,0 +1,516 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * based on exynos_drm_drv.c
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <asm/dma-iommu.h>
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_fb_helper.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/of_graph.h>
>> +#include <linux/component.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_fb.h"
>> +#include "rockchip_drm_fbdev.h"
>> +#include "rockchip_drm_gem.h"
>> +
>> +#define DRIVER_NAME "rockchip"
>> +#define DRIVER_DESC "RockChip Soc DRM"
>> +#define DRIVER_DATE "20140818"
>> +#define DRIVER_MAJOR 1
>> +#define DRIVER_MINOR 0
>> +
>> +/*
>> + * Attach a (component) device to the shared drm dma mapping from master drm
>> + * device. This is used by the VOPs to map GEM buffers to a common DMA
>> + * mapping.
>> + */
>> +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>> + struct device *dev)
>> +{
>> + struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
>> + int ret;
>> +
>> + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
>> + if (ret)
>> + return ret;
>> +
>> + dma_set_max_seg_size(dev, 0xffffffffu);
> Perhaps:
> dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
>
OK.
>> +
>> + return arm_iommu_attach_device(dev, mapping);
>> +}
>> +
>> +void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>> + struct device *dev)
>> +{
>> + arm_iommu_detach_device(drm_dev->dev);
>> +}
>> +
>> +static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
>> +{
>> + struct rockchip_drm_private *private;
>> + struct dma_iommu_mapping *mapping;
>> + struct device *dev = drm_dev->dev;
>> + int ret;
>> +
>> + private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
>> + if (!private)
>> + return -ENOMEM;
>> +
>> + drm_dev->dev_private = private;
>> +
>> + drm_mode_config_init(drm_dev);
>> +
>> + rockchip_drm_mode_config_init(drm_dev);
>> +
>> + dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
>> + GFP_KERNEL);
>> + if (!dev->dma_parms) {
>> + ret = -ENOMEM;
>> + goto err_config_cleanup;
>> + }
>> +
>> + /* TODO(djkurtz): fetch the mapping start/size from somewhere */
>> + mapping = arm_iommu_create_mapping(&platform_bus_type, 0x10000000,
>> + SZ_1G);
>
> Can we make this bigger, and aligned to the requested size?
> I find it makes it easier to debug that way.
>
> How about:
> mapping = arm_iommu_create_mapping(&platform_bus_type, 0x80000000, SZ_2G);
>
> In fact, is there any reason why we do not just make this a 4GB mapping starting
> at 0x00000000? Is that even possible?
OK, We can make mapping start at 0x00000000
>
>
>> + if (IS_ERR(mapping)) {
>> + ret = PTR_ERR(mapping);
>> + goto err_config_cleanup;
>> + }
>> +
>> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
>> + if (ret)
>> + goto err_release_mapping;
>> +
>> + dma_set_max_seg_size(dev, 0xffffffffu);
> see above.
>
OK
>> +
>> + ret = arm_iommu_attach_device(dev, mapping);
>> + if (ret)
>> + goto err_release_mapping;
>> +
>> + /* Try to bind all sub drivers. */
>> + ret = component_bind_all(dev, drm_dev);
>> + if (ret)
>> + goto err_detach_device;
>> +
>> + /* init kms poll for handling hpd */
>> + drm_kms_helper_poll_init(drm_dev);
>> +
>> + /*
>> + * enable drm irq mode.
>> + * - with irq_enabled = true, we can use the vblank feature.
>> + */
>> + drm_dev->irq_enabled = true;
>> +
>> + /*
>> + * with vblank_disable_allowed = true, vblank interrupt will be disabled
>> + * by drm timer once a current process gives up ownership of
>> + * vblank event.(after drm_vblank_put function is called)
>> + */
>> + drm_dev->vblank_disable_allowed = true;
>> +
>> + ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
>> + if (ret)
>> + goto err_kms_helper_poll_fini;
>> +
>> + rockchip_drm_fbdev_init(drm_dev);
>> +
>> + /* force connectors detection */
>> + drm_helper_hpd_irq_event(drm_dev);
>> +
>> + return 0;
>> +
>> +err_kms_helper_poll_fini:
>> + drm_kms_helper_poll_fini(drm_dev);
>> + component_unbind_all(dev, drm_dev);
>> +err_detach_device:
>> + arm_iommu_detach_device(dev);
>> +err_release_mapping:
>> + arm_iommu_release_mapping(dev->archdata.mapping);
>> +err_config_cleanup:
>> + drm_mode_config_cleanup(drm_dev);
>> + drm_dev->dev_private = NULL;
>> + return ret;
>> +}
>> +
>> +static int rockchip_drm_unload(struct drm_device *drm_dev)
>> +{
>> + struct device *dev = drm_dev->dev;
>> +
>> + drm_kms_helper_poll_fini(drm_dev);
>> + component_unbind_all(dev, drm_dev);
>> + arm_iommu_detach_device(dev);
>> + arm_iommu_release_mapping(dev->archdata.mapping);
>> + drm_mode_config_cleanup(drm_dev);
>> + drm_dev->dev_private = NULL;
>> +
>> + return 0;
>> +}
>> +
>> +void rockchip_drm_lastclose(struct drm_device *dev)
>> +{
>> + struct rockchip_drm_private *priv = dev->dev_private;
>> +
>> + if (priv->fb_helper)
>> + drm_fb_helper_restore_fbdev_mode_unlocked(priv->fb_helper);
>> +}
>> +
>> +static const struct file_operations rockchip_drm_driver_fops = {
>> + .owner = THIS_MODULE,
>> + .open = drm_open,
>> + .mmap = rockchip_drm_gem_mmap,
>> + .poll = drm_poll,
>> + .read = drm_read,
>> + .unlocked_ioctl = drm_ioctl,
>> +#ifdef CONFIG_COMPAT
>> + .compat_ioctl = drm_compat_ioctl,
>> +#endif
>> + .release = drm_release,
>> +};
>> +
>> +const struct vm_operations_struct rockchip_drm_vm_ops = {
>> + .open = drm_gem_vm_open,
>> + .close = drm_gem_vm_close,
>> +};
>> +
>> +static struct drm_driver rockchip_drm_driver = {
>> + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
>> + .load = rockchip_drm_load,
>> + .unload = rockchip_drm_unload,
>> + .lastclose = rockchip_drm_lastclose,
>> + .get_vblank_counter = drm_vblank_count,
>> + .enable_vblank = rockchip_drm_crtc_enable_vblank,
>> + .disable_vblank = rockchip_drm_crtc_disable_vblank,
>> + .gem_vm_ops = &rockchip_drm_vm_ops,
>> + .gem_free_object = rockchip_gem_free_object,
>> + .dumb_create = rockchip_gem_dumb_create,
>> + .dumb_map_offset = rockchip_gem_dumb_map_offset,
>> + .dumb_destroy = drm_gem_dumb_destroy,
>> + .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
>> + .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
>> + .gem_prime_import = drm_gem_prime_import,
>> + .gem_prime_export = drm_gem_prime_export,
>> + .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
>> + .gem_prime_vmap = rockchip_gem_prime_vmap,
>> + .gem_prime_vunmap = rockchip_gem_prime_vunmap,
>> + .gem_prime_mmap = rockchip_gem_prime_mmap,
>> + .fops = &rockchip_drm_driver_fops,
>> + .name = DRIVER_NAME,
>> + .desc = DRIVER_DESC,
>> + .date = DRIVER_DATE,
>> + .major = DRIVER_MAJOR,
>> + .minor = DRIVER_MINOR,
>> +};
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
>> +{
>> + struct drm_connector *connector;
>> +
>> + drm_modeset_lock_all(dev);
>> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
>> + int old_dpms = connector->dpms;
>> +
>> + if (connector->funcs->dpms)
>> + connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
>> +
>> + /* Set the old mode back to the connector for resume */
>> + connector->dpms = old_dpms;
>> + }
>> + drm_modeset_unlock_all(dev);
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_drm_resume(struct drm_device *dev)
>> +{
>> + struct drm_connector *connector;
>> +
>> + drm_modeset_lock_all(dev);
>> + list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
>> + if (connector->funcs->dpms)
>> + connector->funcs->dpms(connector, connector->dpms);
>> + }
>> + drm_modeset_unlock_all(dev);
>> +
>> + drm_helper_resume_force_mode(dev);
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_drm_sys_suspend(struct device *dev)
>> +{
>> + struct drm_device *drm_dev = dev_get_drvdata(dev);
>> + pm_message_t message;
>> +
>> + if (pm_runtime_suspended(dev))
>> + return 0;
>> +
>> + message.event = PM_EVENT_SUSPEND;
>> +
>> + return rockchip_drm_suspend(drm_dev, message);
>> +}
>> +
>> +static int rockchip_drm_sys_resume(struct device *dev)
>> +{
>> + struct drm_device *drm_dev = dev_get_drvdata(dev);
>> +
>> + if (!pm_runtime_suspended(dev))
>> + return 0;
>> +
>> + return rockchip_drm_resume(drm_dev);
>> +}
>> +#endif
>> +
>> +static const struct dev_pm_ops rockchip_drm_pm_ops = {
>> + SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
>> + rockchip_drm_sys_resume)
>> +};
>> +
>> +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
>> + struct device_node *np)
>> +{
>> + struct rockchip_drm_private *priv = drm->dev_private;
>> + struct device_node *port;
>> + int pipe;
>> +
>> + if (priv->num_pipe >= ROCKCHIP_MAX_CRTC)
>> + return -EINVAL;
>> +
>> + port = of_get_child_by_name(np, "port");
>> + if (!port) {
>> + dev_err(drm->dev, "no port node found in %s\n",
>> + np->full_name);
>> + return -ENXIO;
>> + }
>> + pipe = priv->num_pipe++;
>> + crtc->port = port;
>> +
>> + priv->crtc[pipe] = crtc;
>> +
>> + return pipe;
>> +}
>> +
>> +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe)
>> +{
>> + struct rockchip_drm_private *priv = drm->dev_private;
>> +
>> + priv->num_pipe--;
>> + of_node_put(priv->crtc[pipe]->port);
>> + priv->crtc[pipe] = NULL;
>> +}
>> +
>> +struct drm_crtc *rockchip_drm_find_crtc(struct drm_device *drm, int pipe)
>> +{
>> + struct rockchip_drm_private *priv = drm->dev_private;
>> +
>> + if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe])
>> + return priv->crtc[pipe];
>> +
>> + return NULL;
>> +}
>> +
>> +/*
>> + * @node: device tree node containing encoder input ports
>> + * @encoder: drm_encoder
>> + */
>> +int rockchip_drm_encoder_get_mux_id(struct device_node *node,
>> + struct drm_encoder *encoder)
>> +{
>> + struct device_node *ep = NULL;
>> + struct drm_crtc *crtc = encoder->crtc;
>> + struct of_endpoint endpoint;
>> + struct device_node *port;
>> + int ret;
>> +
>> + if (!node || !crtc)
>> + return -EINVAL;
>> +
>> + do {
>> + ep = of_graph_get_next_endpoint(node, ep);
>> + if (!ep)
>> + break;
>> +
>> + port = of_graph_get_remote_port(ep);
>> + of_node_put(port);
>> + if (port == crtc->port) {
>> + ret = of_graph_parse_endpoint(ep, &endpoint);
>> + return ret ?: endpoint.id;
>> + }
>> + } while (ep);
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static int compare_of(struct device *dev, void *data)
>> +{
>> + struct device_node *np = data;
>> +
>> + return dev->of_node == np;
>> +}
>> +
>> +static void rockchip_add_endpoints(struct device *dev,
>> + struct component_match **match,
>> + struct device_node *port)
>> +{
>> + struct device_node *ep, *remote;
>> +
>> + for_each_child_of_node(port, ep) {
>> + remote = of_graph_get_remote_port_parent(ep);
>> + if (!remote || !of_device_is_available(remote)) {
>> + of_node_put(remote);
>> + continue;
>> + } else if (!of_device_is_available(remote->parent)) {
>> + dev_warn(dev, "parent device of %s is not available\n",
>> + remote->full_name);
>> + of_node_put(remote);
>> + continue;
>> + }
>> +
>> + component_match_add(dev, match, compare_of, remote);
>> + of_node_put(remote);
>> + }
>> +}
>> +
>> +static int rockchip_drm_bind(struct device *dev)
>> +{
>> + struct drm_device *drm;
>> + int ret;
>> +
>> + drm = drm_dev_alloc(&rockchip_drm_driver, dev);
>> + if (!drm)
>> + return -ENOMEM;
>> +
>> + ret = drm_dev_set_unique(drm, "%s", dev_name(dev));
>> + if (ret)
>> + goto err_free;
>> +
>> + ret = drm_dev_register(drm, 0);
>> + if (ret)
>> + goto err_free;
>> +
>> + dev_set_drvdata(dev, drm);
>> +
>> + return 0;
>> +
>> +err_free:
>> + drm_dev_unref(drm);
>> + return ret;
>> +}
>> +
>> +static void rockchip_drm_unbind(struct device *dev)
>> +{
>> + struct drm_device *drm = dev_get_drvdata(dev);
>> +
>> + drm_dev_unregister(drm);
>> + drm_dev_unref(drm);
>> +}
>> +
>> +static const struct component_master_ops rockchip_drm_ops = {
>> + .bind = rockchip_drm_bind,
>> + .unbind = rockchip_drm_unbind,
>> +};
>> +
>> +static int rockchip_drm_platform_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct component_match *match = NULL;
>> + struct device_node *np = dev->of_node;
>> + struct device_node *port;
>> + int i;
>> + int ret;
>> +
>> + if (!np)
>> + return -ENODEV;
>> + /*
>> + * Bind the crtc ports first, so that
>> + * drm_of_find_possible_crtcs called from encoder .bind callbacks
>> + * works as expected.
>> + */
>> + for (i = 0;; i++) {
>> + port = of_parse_phandle(np, "ports", i);
>> + if (!port)
>> + break;
>> +
>> + 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 == 0) {
>> + dev_err(dev, "missing 'ports' property\n");
>> + return -ENODEV;
>> + }
>> + /*
>> + * For each bound crtc, bind the encoders attached to its
>> + * remote endpoint.
>> + */
>> + for (i = 0;; i++) {
>> + port = of_parse_phandle(np, "ports", i);
>> + if (!port)
>> + break;
>> +
>> + if (!of_device_is_available(port->parent)) {
>> + of_node_put(port);
>> + continue;
>> + }
>> +
>> + rockchip_add_endpoints(dev, &match, port);
>> + of_node_put(port);
>> + }
>> +
>> + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
>> + if (ret)
>> + return ret;
> This is in drm_load() now.
OK
>> +
>> + return component_master_add_with_match(dev, &rockchip_drm_ops, match);
>> +}
>> +
>> +static int rockchip_drm_platform_remove(struct platform_device *pdev)
>> +{
>> + component_master_del(&pdev->dev, &rockchip_drm_ops);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id rockchip_drm_dt_ids[] = {
>> + { .compatible = "rockchip,display-subsystem", },
>> + { /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
>> +
>> +static struct platform_driver rockchip_drm_platform_driver = {
>> + .probe = rockchip_drm_platform_probe,
>> + .remove = rockchip_drm_platform_remove,
>> + .driver = {
>> + .owner = THIS_MODULE,
>> + .name = "rockchip-drm",
>> + .of_match_table = rockchip_drm_dt_ids,
>> + .pm = &rockchip_drm_pm_ops,
>> + },
>> +};
>> +
>> +module_platform_driver(rockchip_drm_platform_driver);
>> +
>> +MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
>> +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..98094ecf
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -0,0 +1,61 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * based on exynos_drm_drv.h
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef _ROCKCHIP_DRM_DRV_H
>> +#define _ROCKCHIP_DRM_DRV_H
>> +
>> +#include <linux/module.h>
>> +#include <linux/component.h>
>> +
>> +#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_crtc.
>> + * @num_pipe: number of pipes for this device.
>> + */
>> +struct rockchip_drm_private {
>> + struct drm_fb_helper *fb_helper;
>> + /*
>> + * created crtc object would be contained at this array and
>> + * this array is used to be aware of which crtc did it request vblank.
>> + */
>> + struct drm_crtc *crtc[ROCKCHIP_MAX_CRTC];
>> +
>> + unsigned int num_pipe;
>> +};
>> +
>> +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
>> + struct device_node *port);
>> +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe);
>> +struct drm_crtc *rockchip_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 connector_type);
>> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
>> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
>> +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>> + struct device *dev);
>> +void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>> + struct device *dev);
>> +
>> +#endif /* _ROCKCHIP_DRM_DRV_H_ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> new file mode 100644
>> index 0000000..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 <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <drm/drm.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_fb_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_gem.h"
>> +
>> +#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>> +
>> +struct rockchip_drm_fb {
>> + struct drm_framebuffer fb;
>> + struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
>> +};
>> +
>> +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
>> + unsigned int plane)
>> +{
>> + struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
>> +
>> + if (plane >= ROCKCHIP_MAX_FB_BUFFER)
>> + return NULL;
>> +
>> + return rk_fb->obj[plane];
>> +}
>> +
>> +static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
>> +{
>> + struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
>> + struct drm_gem_object *obj;
>> + int i;
>> +
>> + for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
>> + obj = rockchip_fb->obj[i];
>> + if (obj)
>> + drm_gem_object_unreference_unlocked(obj);
>> + }
>> +
>> + drm_framebuffer_cleanup(fb);
>> + kfree(rockchip_fb);
>> +}
>> +
>> +static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>> + struct drm_file *file_priv,
>> + unsigned int *handle)
>> +{
>> + struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
>> +
>> + return drm_gem_handle_create(file_priv,
>> + rockchip_fb->obj[0], handle);
>> +}
>> +
>> +static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>> + .destroy = rockchip_drm_fb_destroy,
>> + .create_handle = rockchip_drm_fb_create_handle,
>> +};
>> +
>> +static struct rockchip_drm_fb *
>> +rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
>> + struct drm_gem_object **obj, unsigned int num_planes)
>> +{
>> + struct rockchip_drm_fb *rockchip_fb;
>> + int ret;
>> + int i;
>> +
>> + rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
>> + if (!rockchip_fb)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
>> +
>> + for (i = 0; i < num_planes; i++)
>> + rockchip_fb->obj[i] = obj[i];
>> +
>> + ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
>> + &rockchip_drm_fb_funcs);
>> + if (ret) {
>> + dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
>> + ret);
>> + kfree(rockchip_fb);
>> + return ERR_PTR(ret);
>> + }
>> +
>> + return rockchip_fb;
>> +}
>> +
>> +static struct drm_framebuffer *
>> +rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
>> + struct drm_mode_fb_cmd2 *mode_cmd)
>> +{
>> + struct rockchip_drm_fb *rockchip_fb;
>> + struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
>> + struct drm_gem_object *obj;
>> + unsigned int hsub;
>> + unsigned int vsub;
>> + int num_planes;
>> + int ret;
>> + int i;
>> +
>> + hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
>> + vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
>> + num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
>> + ROCKCHIP_MAX_FB_BUFFER);
>> +
>> + for (i = 0; i < num_planes; i++) {
>> + unsigned int width = mode_cmd->width / (i ? hsub : 1);
>> + unsigned int height = mode_cmd->height / (i ? vsub : 1);
>> + unsigned int min_size;
>> +
>> + obj = drm_gem_object_lookup(dev, file_priv,
>> + mode_cmd->handles[i]);
>> + if (!obj) {
>> + dev_err(dev->dev, "Failed to lookup GEM object\n");
>> + ret = -ENXIO;
>> + goto err_gem_object_unreference;
>> + }
>> +
>> + min_size = (height - 1) * mode_cmd->pitches[i] +
>> + mode_cmd->offsets[i] +
>> + width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
>> +
>> + if (obj->size < min_size) {
>> + drm_gem_object_unreference_unlocked(obj);
>> + ret = -EINVAL;
>> + goto err_gem_object_unreference;
>> + }
>> + objs[i] = obj;
>> + }
>> +
>> + rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
>> + if (IS_ERR(rockchip_fb)) {
>> + ret = PTR_ERR(rockchip_fb);
>> + goto err_gem_object_unreference;
>> + }
>> +
>> + return &rockchip_fb->fb;
>> +
>> +err_gem_object_unreference:
>> + for (i--; i >= 0; i--)
>> + drm_gem_object_unreference_unlocked(objs[i]);
>> + return ERR_PTR(ret);
>> +}
>> +
>> +static void rockchip_drm_output_poll_changed(struct drm_device *dev)
>> +{
>> + struct rockchip_drm_private *private = dev->dev_private;
>> + struct drm_fb_helper *fb_helper = private->fb_helper;
>> +
>> + if (fb_helper)
>> + drm_fb_helper_hotplug_event(fb_helper);
>> +}
>> +
>> +static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
>> + .fb_create = rockchip_user_fb_create,
>> + .output_poll_changed = rockchip_drm_output_poll_changed,
>> +};
>> +
>> +struct drm_framebuffer *
>> +rockchip_drm_framebuffer_init(struct drm_device *dev,
>> + struct drm_mode_fb_cmd2 *mode_cmd,
>> + struct drm_gem_object *obj)
>> +{
>> + struct rockchip_drm_fb *rockchip_fb;
>> +
>> + rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
>> + if (IS_ERR(rockchip_fb))
>> + return NULL;
>> +
>> + return &rockchip_fb->fb;
>> +}
>> +
>> +void rockchip_drm_mode_config_init(struct drm_device *dev)
>> +{
>> + dev->mode_config.min_width = 0;
>> + dev->mode_config.min_height = 0;
>> +
>> + /*
>> + * set max width and height as default value(4096x4096).
>> + * this value would be used to check framebuffer size limitation
>> + * at drm_mode_addfb().
>> + */
>> + dev->mode_config.max_width = 4096;
>> + dev->mode_config.max_height = 4096;
>> +
>> + dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
>> +}
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
>> new file mode 100644
>> index 0000000..09574d4
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
>> @@ -0,0 +1,28 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef _ROCKCHIP_DRM_FB_H
>> +#define _ROCKCHIP_DRM_FB_H
>> +
>> +struct drm_framebuffer *
>> +rockchip_drm_framebuffer_init(struct drm_device *dev,
>> + struct drm_mode_fb_cmd2 *mode_cmd,
>> + struct drm_gem_object *obj);
>> +void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
>> +
>> +void rockchip_drm_mode_config_init(struct drm_device *dev);
>> +
>> +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
>> + unsigned int plane);
>> +#endif /* _ROCKCHIP_DRM_FB_H */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
>> new file mode 100644
>> index 0000000..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 <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <drm/drm.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_fb_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_gem.h"
>> +#include "rockchip_drm_fb.h"
>> +
>> +#define PREFERRED_BPP 32
>> +#define to_rockchip_fbdev(x) container_of(x, struct rockchip_fbdev, helper)
>> +
>> +struct rockchip_fbdev {
>> + struct drm_fb_helper helper;
>> + struct drm_gem_object *bo;
>> +};
>> +
>> +static int rockchip_fbdev_mmap(struct fb_info *info,
>> + struct vm_area_struct *vma)
>> +{
>> + struct drm_fb_helper *helper = info->par;
>> + struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
>> +
>> + return rockchip_gem_mmap(fbdev->bo, vma);
>> +}
>> +
>> +static struct fb_ops rockchip_drm_fbdev_ops = {
>> + .owner = THIS_MODULE,
>> + .fb_mmap = rockchip_fbdev_mmap,
>> + .fb_fillrect = cfb_fillrect,
>> + .fb_copyarea = cfb_copyarea,
>> + .fb_imageblit = cfb_imageblit,
>> + .fb_check_var = drm_fb_helper_check_var,
>> + .fb_set_par = drm_fb_helper_set_par,
>> + .fb_blank = drm_fb_helper_blank,
>> + .fb_pan_display = drm_fb_helper_pan_display,
>> + .fb_setcmap = drm_fb_helper_setcmap,
>> +};
>> +
>> +static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
>> + struct drm_fb_helper_surface_size *sizes)
>> +{
>> + struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
>> + struct drm_mode_fb_cmd2 mode_cmd = { 0 };
>> + struct drm_device *dev = helper->dev;
>> + struct rockchip_gem_object *rk_obj;
>> + struct drm_framebuffer *fb;
>> + unsigned int bytes_per_pixel;
>> + unsigned long offset;
>> + struct fb_info *fbi;
>> + size_t size;
>> + int ret;
>> +
>> + bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
>> +
>> + mode_cmd.width = sizes->surface_width;
>> + mode_cmd.height = sizes->surface_height;
>> + mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
>> + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
>> + sizes->surface_depth);
>> +
>> + size = mode_cmd.pitches[0] * mode_cmd.height;
>> +
>> + rk_obj = rockchip_gem_create_object(dev, size);
>> + if (IS_ERR(rk_obj))
>> + return -ENOMEM;
>> +
>> + fbdev->bo = &rk_obj->base;
>> +
>> + fbi = framebuffer_alloc(0, dev->dev);
>> + if (!fbi) {
>> + dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
>> + ret = -ENOMEM;
>> + goto err_rockchip_gem_free_object;
>> + }
>> +
>> + helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, fbdev->bo);
>> + if (IS_ERR(helper->fb)) {
>> + dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
>> + ret = PTR_ERR(helper->fb);
>> + goto err_framebuffer_release;
>> + }
>> +
>> + helper->fbdev = fbi;
>> +
>> + fbi->par = helper;
>> + fbi->flags = FBINFO_FLAG_DEFAULT;
>> + fbi->fbops = &rockchip_drm_fbdev_ops;
>> +
>> + ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
>> + if (ret) {
>> + dev_err(dev->dev, "Failed to allocate color map.\n");
>> + goto err_drm_framebuffer_unref;
>> + }
>> +
>> + fb = helper->fb;
>> + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
>> + drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
>> +
>> + offset = fbi->var.xoffset * bytes_per_pixel;
>> + offset += fbi->var.yoffset * fb->pitches[0];
>> +
>> + dev->mode_config.fb_base = 0;
>> + fbi->screen_base = rk_obj->kvaddr + offset;
>> + fbi->screen_size = rk_obj->base.size;
>> + fbi->fix.smem_len = rk_obj->base.size;
>> +
>> + DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
>> + fb->width, fb->height, fb->depth, rk_obj->kvaddr,
>> + offset, size);
>> + return 0;
>> +
>> +err_drm_framebuffer_unref:
>> + drm_framebuffer_unreference(helper->fb);
>> +err_framebuffer_release:
>> + framebuffer_release(fbi);
>> +err_rockchip_gem_free_object:
>> + rockchip_gem_free_object(&rk_obj->base);
>> + return ret;
>> +}
>> +
>> +static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
>> + .fb_probe = rockchip_drm_fbdev_create,
>> +};
>> +
>> +int rockchip_drm_fbdev_init(struct drm_device *dev)
>> +{
>> + struct rockchip_drm_private *private = dev->dev_private;
>> + struct rockchip_fbdev *fbdev;
>> + struct drm_fb_helper *helper;
>> + unsigned int num_crtc;
>> + int ret;
>> +
>> + if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
>> + return -EINVAL;
>> +
>> + if (private->fb_helper) {
>> + DRM_ERROR("no allow to reinit fbdev\n");
>> + return -EINVAL;
>> + }
>> +
>> + num_crtc = dev->mode_config.num_crtc;
>> +
>> + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
> Can you just embed rockchip_fbdev in struct rockchip_drm_private...
> Or, just add its two fields to rockchip_drm_private:
>
> struct drm_fb_helper fbdev_helper;
> struct drm_gem_object *fbdev_bo;
>
> It would eliminate some of the complexity in _fbdev_init, and especially in
> _fbdev_fini.
OK.
>
>> + if (!fbdev)
>> + return -ENOMEM;
>> +
>> + fbdev->helper.funcs = &rockchip_drm_fb_helper_funcs;
>> + helper = &fbdev->helper;
>> +
>> + ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
>> + if (ret < 0) {
>> + dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
>> + goto err_free;
>> + }
>> +
>> + ret = drm_fb_helper_single_add_all_connectors(helper);
>> + if (ret < 0) {
>> + dev_err(dev->dev, "Failed to add connectors.\n");
>> + goto err_drm_fb_helper_fini;
>> + }
>> +
>> + /* disable all the possible outputs/crtcs before entering KMS mode */
>> + drm_helper_disable_unused_functions(dev);
>> +
>> + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
>> + if (ret < 0) {
>> + dev_err(dev->dev, "Failed to set initial hw configuration.\n");
>> + goto err_drm_fb_helper_fini;
>> + }
>> +
>> + private->fb_helper = helper;
>> +
>> + return 0;
>> +
>> +err_drm_fb_helper_fini:
>> + drm_fb_helper_fini(helper);
>> +err_free:
>> + kfree(fbdev);
>> + return ret;
>> +}
>> +
>> +void rockchip_drm_fbdev_fini(struct drm_device *dev)
>> +{
>> + struct rockchip_drm_private *private = dev->dev_private;
>> + struct drm_fb_helper *helper;
>> + struct rockchip_fbdev *fbdev;
>> +
>> + if (!private || !private->fb_helper)
>> + return;
> Can these ever be NULL here?
maybe can't be NULL if fbdev_init()/fbdev_fini() in pairs.
>> +
>> + helper = private->fb_helper;
>> + fbdev = to_rockchip_fbdev(helper);
>> +
>> + if (helper->fbdev) {
>> + struct fb_info *info;
>> + int ret;
>> +
>> + info = helper->fbdev;
>> + ret = unregister_framebuffer(info);
>> + if (ret < 0)
>> + DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
>> +
>> + if (info->cmap.len)
>> + fb_dealloc_cmap(&info->cmap);
>> +
>> + framebuffer_release(info);
>> + }
>> +
>> + if (helper->fb)
>> + drm_framebuffer_unreference(helper->fb);
>> +
>> + drm_fb_helper_fini(helper);
>> + kfree(fbdev);
>> + private->fb_helper = NULL;
>> +}
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
>> new file mode 100644
>> index 0000000..5edcf6a
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
>> @@ -0,0 +1,20 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef _ROCKCHIP_DRM_FBDEV_H
>> +#define _ROCKCHIP_DRM_FBDEV_H
>> +
>> +int rockchip_drm_fbdev_init(struct drm_device *dev);
>> +
>> +#endif /* _ROCKCHIP_DRM_FBDEV_H */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
>> new file mode 100644
>> index 0000000..5563dff
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
>> @@ -0,0 +1,345 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <drm/drm.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_vma_manager.h>
>> +
>> +#include <linux/anon_inodes.h>
>> +#include <linux/dma-attrs.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_gem.h"
>> +
>> +static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj)
>> +{
>> + struct drm_gem_object *obj = &rk_obj->base;
>> + struct drm_device *drm = obj->dev;
>> +
>> + init_dma_attrs(&rk_obj->dma_attrs);
>> + dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
>> +
>> + /* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
>> + rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
>> + &rk_obj->dma_addr, GFP_KERNEL,
>> + &rk_obj->dma_attrs);
>> + if (IS_ERR(rk_obj->kvaddr)) {
>> + int ret = PTR_ERR(rk_obj->kvaddr);
>> +
>> + DRM_ERROR("failed to allocate %#x byte dma buffer, %d",
>> + obj->size, ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
>> +{
>> + struct drm_gem_object *obj = &rk_obj->base;
>> + struct drm_device *drm = obj->dev;
>> +
>> + dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
>> + &rk_obj->dma_attrs);
>> +}
>> +
>> +/* drm driver mmap file operations */
>> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
>> +{
>> + struct drm_file *priv = filp->private_data;
>> + struct drm_device *dev = priv->minor->dev;
>> + struct drm_gem_object *obj;
>> + struct drm_vma_offset_node *node;
>> + int ret;
>> +
>> + if (drm_device_is_unplugged(dev))
>> + return -ENODEV;
>> +
>> + mutex_lock(&dev->struct_mutex);
>> +
>> + node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
>> + vma->vm_pgoff,
>> + vma_pages(vma));
>> + if (!node) {
>> + mutex_unlock(&dev->struct_mutex);
>> + DRM_ERROR("failed to find vma node.\n");
>> + return -EINVAL;
>> + } else if (!drm_vma_node_is_allowed(node, filp)) {
>> + mutex_unlock(&dev->struct_mutex);
>> + return -EACCES;
>> + }
>> +
>> + obj = container_of(node, struct drm_gem_object, vma_node);
>> + ret = rockchip_gem_mmap(obj, vma);
>> +
>> + mutex_unlock(&dev->struct_mutex);
>> +
>> + return ret;
>> +}
>> +
>> +int rockchip_drm_gem_mmap_buffer(struct file *filp,
>> + struct vm_area_struct *vma)
>> +{
>> + struct drm_gem_object *obj = filp->private_data;
>> +
>> + return rockchip_gem_mmap(obj, vma);
>> +}
>> +
>> +static const struct file_operations rockchip_drm_gem_fops = {
>> + .mmap = rockchip_drm_gem_mmap_buffer,
>> +};
>> +
>> +struct rockchip_gem_object *
>> + rockchip_gem_create_object(struct drm_device *drm, unsigned int size)
>> +{
>> + struct rockchip_gem_object *rk_obj;
>> + struct drm_gem_object *obj;
>> + struct file *filp;
>> + int ret;
>> +
>> + size = round_up(size, PAGE_SIZE);
>> +
>> + rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
>> + if (!rk_obj)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + obj = &rk_obj->base;
>> +
>> + drm_gem_private_object_init(drm, obj, size);
>> +
>> + filp = anon_inode_getfile("rockchip_gem", &rockchip_drm_gem_fops,
>> + obj, 0);
>> + if (IS_ERR(filp)) {
>> + DRM_ERROR("failed to create anon file object.\n");
>> + ret = PTR_ERR(filp);
>> + goto err_free_rk_obj;
>> + }
>> + filp->f_mode = FMODE_READ | FMODE_WRITE;
>> + obj->filp = filp;
> I think this chunk goes away now, if we do not have custom gem mmap ioctl? an
> Along with rockchip_drm_gem_fops and rockchip_drm_gem_mmap_buffer.
>
>
Oh, yes, now this chunk is not in use, I will just let it go away.
>> +
>> + ret = drm_gem_create_mmap_offset(obj);
>> + if (ret)
>> + goto err_free_obj;
>> +
>> + ret = rockchip_gem_alloc_buf(rk_obj);
>> + if (ret)
>> + goto err_free_mmap_offset;
>> +
>> + return rk_obj;
>> +
>> +err_free_mmap_offset:
>> + drm_gem_free_mmap_offset(obj);
>> +err_free_obj:
>> + drm_gem_object_release(obj);
>> +err_free_rk_obj:
>> + kfree(rk_obj);
>> + return ERR_PTR(ret);
>> +}
>> +
>> +/*
>> + * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
>> + * function
>> + */
>> +void rockchip_gem_free_object(struct drm_gem_object *obj)
>> +{
>> + struct rockchip_gem_object *rk_obj;
>> +
>> + drm_gem_free_mmap_offset(obj);
>> +
>> + rk_obj = to_rockchip_obj(obj);
>> +
>> + rockchip_gem_free_buf(rk_obj);
>> +
>> + drm_gem_object_release(obj);
>> +
>> + kfree(rk_obj);
>> +}
>> +
>> +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
> Nit: Can you move this function up just below rockchip_gem_free_buf.
> That would put these three dma_*_attrs() wrapper functions in one place.
OK
>> +{
>> + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
>> + struct drm_device *drm = obj->dev;
>> + unsigned long vm_size;
>> +
>> + vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
>> + vm_size = vma->vm_end - vma->vm_start;
>> +
>> + if (vm_size > obj->size)
>> + return -EINVAL;
>> +
>> + return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
>> + obj->size, &rk_obj->dma_attrs);
>> +}
>> +
>> +/*
>> + * rockchip_gem_create_with_handle - allocate an object with the given
>> + * size and create a gem handle on it
>> + *
>> + * returns a struct rockchip_gem_object* on success or ERR_PTR values
>> + * on failure.
>> + */
>> +static struct rockchip_gem_object *
>> +rockchip_gem_create_with_handle(struct drm_file *file_priv,
>> + struct drm_device *drm, unsigned int size,
>> + unsigned int *handle)
>> +{
>> + struct rockchip_gem_object *rk_obj;
>> + struct drm_gem_object *obj;
>> + int ret;
>> +
>> + rk_obj = rockchip_gem_create_object(drm, size);
>> + if (IS_ERR(rk_obj))
>> + return NULL;
> Should this be:
> return ERR_CAST(rk_obj);
OK.
>> +
>> + obj = &rk_obj->base;
>> +
>> + /*
>> + * allocate a id of idr table where the obj is registered
>> + * and handle has the id what user can see.
>> + */
>> + ret = drm_gem_handle_create(file_priv, obj, handle);
>> + if (ret)
>> + goto err_handle_create;
>> +
>> + /* drop reference from allocate - handle holds it now. */
>> + drm_gem_object_unreference_unlocked(obj);
>> +
>> + return rk_obj;
>> +
>> +err_handle_create:
>> + rockchip_gem_free_object(obj);
>> +
>> + return ERR_PTR(ret);
>> +}
>> +
>> +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
>> + struct drm_device *dev, uint32_t handle,
>> + uint64_t *offset)
>> +{
>> + struct drm_gem_object *obj;
>> + int ret = 0;
> Nit: No = 0;
>
> (I am a fan of letting the compiler tell us if we forget to initialize a
> return value in goto/error paths)
that is cool.
>> +
>> + 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.
>> + */
> Just delete this comment that was copied from exynos.
OK.
>> +
>> + obj = drm_gem_object_lookup(dev, file_priv, handle);
>> + if (!obj) {
>> + DRM_ERROR("failed to lookup gem object.\n");
>> + ret = -EINVAL;
>> + goto unlock;
>> + }
>> +
>> + ret = drm_gem_create_mmap_offset(obj);
>> + if (ret)
>> + goto out;
>> +
>> + *offset = drm_vma_node_offset_addr(&obj->vma_node);
>> + DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
> Nit: print all 64 bits of offset
you are right, unsigned long is 32bit.
>> +
>> +out:
>> + drm_gem_object_unreference(obj);
>> +unlock:
>> + mutex_unlock(&dev->struct_mutex);
>> + return ret;
>> +}
>> +
>> +/*
>> + * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
>> + * function
>> + *
>> + * This aligns the pitch and size arguments to the minimum required. wrap
>> + * this into your own function if you need bigger alignment.
>> + */
>> +int rockchip_gem_dumb_create(struct drm_file *file_priv,
>> + struct drm_device *dev,
>> + struct drm_mode_create_dumb *args)
>> +{
>> + struct rockchip_gem_object *rk_obj;
>> + int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
>> +
>> + /*
>> + * align to 64 bytes since Mali requires it.
>> + */
>> + min_pitch = ((min_pitch + 63) / 64) * 64;
> ALIGN(min_pitch, 64);
OK.
>> +
>> + if (args->pitch < min_pitch)
>> + args->pitch = min_pitch;
>> +
>> + if (args->size < args->pitch * args->height)
>> + args->size = args->pitch * args->height;
>> +
>> + rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
>> + &args->handle);
>> +
>> + return PTR_ERR_OR_ZERO(rk_obj);
>> +}
>> +
>> +/*
>> + * Allocate a sg_table for this GEM object.
>> + * Note: Both the table's contents, and the sg_table itself must be freed by
>> + * the caller.
>> + * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
>> + */
>> +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
>> +{
>> + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
>> + struct drm_device *drm = obj->dev;
>> + struct sg_table *sgt = NULL;
> Do not init to NULL
>
OK.
>> + int ret;
>> +
>> + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
>> + if (!sgt)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
>> + rk_obj->dma_addr, obj->size,
>> + &rk_obj->dma_attrs);
>> + if (ret) {
>> + DRM_ERROR("failed to allocate sgt, %d\n", ret);
>> + kfree(sgt);
>> + return ERR_PTR(ret);
>> + }
>> +
>> + return sgt;
>> +}
>> +
>> +void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
>> +{
>> + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
>> +
>> + return rk_obj->kvaddr;
>> +}
>> +
>> +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
>> +{
>> + /* Nothing to do */
>> +}
>> +
>> +int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
>> + struct vm_area_struct *vma)
> I don't think this function is correct. I think we should use dma_mmap_attrs()?
> But, you can probably just remove it, and drop support for mmap'ing
> exported dma_bufs for now.
OK, I will drop it.
>> +{
>> + struct drm_device *dev = obj->dev;
>> + int ret;
>> +
>> + mutex_lock(&dev->struct_mutex);
>> + ret = drm_gem_mmap_obj(obj, obj->size, vma);
>> + mutex_unlock(&dev->struct_mutex);
>> +
>> + return ret;
>> +}
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
>> new file mode 100644
>> index 0000000..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 <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef _ROCKCHIP_DRM_GEM_H
>> +#define _ROCKCHIP_DRM_GEM_H
>> +
>> +#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
>> +
>> +struct rockchip_gem_object {
>> + struct drm_gem_object base;
>> + unsigned int flags;
>> +
>> + void *kvaddr;
>> + dma_addr_t dma_addr;
>> + struct dma_attrs dma_attrs;
>> +};
>> +
>> +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
>> +struct drm_gem_object *
>> +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
>> + struct sg_table *sgt);
>> +void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
>> +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
>> +int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
>> + struct vm_area_struct *vma);
>> +
>> +/* drm driver mmap file operations */
>> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
>> +
>> +/* mmap a gem object to userspace. */
>> +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
>> +
>> +struct rockchip_gem_object *
>> + rockchip_gem_create_object(struct drm_device *drm, unsigned int size);
>> +
>> +void rockchip_gem_free_object(struct drm_gem_object *obj);
>> +
>> +int rockchip_gem_dumb_create(struct drm_file *file_priv,
>> + struct drm_device *dev,
>> + struct drm_mode_create_dumb *args);
>> +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
>> + struct drm_device *dev, uint32_t handle,
>> + uint64_t *offset);
>> +#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..fe9d2b0
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -0,0 +1,1422 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <drm/drm.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_crtc.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/component.h>
>> +
>> +#include <linux/reset.h>
>> +#include <linux/iommu.h>
>> +#include <linux/delay.h>
>> +
>> +#include <video/of_display_timing.h>
>> +#include <video/of_videomode.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_fbdev.h"
>> +#include "rockchip_drm_gem.h"
>> +#include "rockchip_drm_fb.h"
>> +#include "rockchip_drm_vop.h"
>> +
>> +#define VOP_DEFAULT_FRAMERATE 60
>> +#define VOP_MAX_WIN_SUPPORT 5
>> +#define VOP_DEFAULT_PRIMARY 0
>> +#define VOP_DEFAULT_CURSOR 1
>> +#define VOP_REG(off, _mask, s) \
>> + {.offset = off, \
>> + .mask = _mask, \
>> + .shift = s,}
>> +
>> +#define __REG_SET(x, off, mask, shift, v) \
>> + vop_mask_write(x, off, (mask) << shift, (v) << shift)
>> +
>> +#define REG_SET(x, base, reg, v) \
>> + __REG_SET(x, base + reg.offset, reg.mask, reg.shift, v)
>> +
>> +#define VOP_WIN_SET(x, win, name, v) \
>> + REG_SET(x, win->base, win->phy->name, v)
>> +#define VOP_CTRL_SET(x, name, v) \
>> + REG_SET(x, 0, (x)->data->ctrl->name, v)
>> +
>> +#define VOP_WIN_GET_YRGBADDR(ctx, win) \
>> + vop_readl(ctx, win->base + win->phy->yrgb_mst.offset)
>> +
>> +#define to_vop_ctx(x) container_of(x, struct vop_context, crtc)
>> +#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
>> +
>> +struct rockchip_plane {
>> + int id;
>> + struct drm_plane base;
>> + const struct vop_win *win;
>> +
>> + uint32_t pending_yrgb_mst;
>> + struct drm_framebuffer *front_fb;
>> + struct drm_framebuffer *pending_fb;
>> + bool enabled;
>> +};
>> +
>> +struct vop_context {
>> + struct device *dev;
>> + struct drm_device *drm_dev;
>> + struct drm_crtc crtc;
>> + struct drm_pending_vblank_event *event;
>> + struct vop_driver *drv;
>> + unsigned int dpms;
>> + unsigned int win_mask;
>> + wait_queue_head_t wait_vsync_queue;
>> + atomic_t wait_vsync_event;
>> +
>> + struct workqueue_struct *vsync_wq;
>> + struct work_struct vsync_work;
>> +
>> + /* mutex vsync_ work */
>> + struct mutex vsync_mutex;
>> + bool vsync_work_pending;
>> +
>> + struct vop_driver_data *data;
>> +
>> + uint32_t *regsbak;
>> + void __iomem *regs;
>> +
>> + /* physical map length of vop register */
>> + uint32_t len;
>> +
>> + /* one time only one process allowed to config the register */
>> + spinlock_t reg_lock;
>> + /* lock vop irq reg */
>> + spinlock_t irq_lock;
>> +
>> + unsigned int irq;
>> +
>> + /* vop AHP clk */
>> + struct clk *hclk;
>> + /* vop dclk */
>> + struct clk *dclk;
>> + /* vop share memory frequency */
>> + struct clk *aclk;
>> +
>> + /* vop ahb reset */
>> + struct reset_control *ahb_rst;
>> + /* vop dclk reset */
>> + struct reset_control *dclk_rst;
>> + uint32_t pixclock;
> Where is pixclock used?
not use now.
>> +
>> + int pipe;
>> + bool clk_on;
>> +};
>> +
>> +enum vop_data_format {
>> + VOP_FMT_ARGB8888 = 0,
>> + VOP_FMT_RGB888,
>> + VOP_FMT_RGB565,
>> + VOP_FMT_YUV420SP = 4,
>> + VOP_FMT_YUV422SP,
>> + VOP_FMT_YUV444SP,
>> +};
>> +
>> +struct vop_reg_data {
>> + uint32_t offset;
>> + uint32_t value;
>> +};
>> +
>> +struct vop_reg {
>> + uint32_t offset;
>> + uint32_t shift;
>> + uint32_t mask;
>> +};
>> +
>> +struct vop_ctrl {
>> + struct vop_reg standby;
>> + struct vop_reg data_blank;
>> + struct vop_reg gate_en;
>> + struct vop_reg mmu_en;
>> + struct vop_reg rgb_en;
>> + struct vop_reg edp_en;
>> + struct vop_reg hdmi_en;
>> + struct vop_reg mipi_en;
>> + struct vop_reg out_mode;
>> + struct vop_reg dither_down;
>> + struct vop_reg dither_up;
>> + struct vop_reg pin_pol;
>> +
>> + struct vop_reg htotal_pw;
>> + struct vop_reg hact_st_end;
>> + struct vop_reg vtotal_pw;
>> + struct vop_reg vact_st_end;
>> + struct vop_reg hpost_st_end;
>> + struct vop_reg vpost_st_end;
>> +};
>> +
>> +struct vop_win_phy {
>> + const uint32_t *data_formats;
>> + uint32_t nformats;
>> +
>> + struct vop_reg enable;
>> + struct vop_reg format;
>> + struct vop_reg act_info;
>> + struct vop_reg dsp_info;
>> + struct vop_reg dsp_st;
>> + struct vop_reg yrgb_mst;
>> + struct vop_reg uv_mst;
>> + struct vop_reg yrgb_vir;
>> + struct vop_reg uv_vir;
>> +
>> + struct vop_reg dst_alpha_ctl;
>> + struct vop_reg src_alpha_ctl;
>> +};
>> +
>> +struct vop_win {
>> + uint32_t base;
>> + const struct vop_win_phy *phy;
>> +};
>> +
>> +struct vop_driver_data {
>> + const void *init_table;
>> + int table_size;
>> + const struct vop_ctrl *ctrl;
>> + const struct vop_win *win[VOP_MAX_WIN_SUPPORT];
>> +};
>> +
>> +static const uint32_t formats_01[] = {
>> + DRM_FORMAT_XRGB8888,
>> + DRM_FORMAT_ARGB8888,
>> + DRM_FORMAT_RGB888,
>> + DRM_FORMAT_RGB565,
>> + DRM_FORMAT_NV12,
>> + DRM_FORMAT_NV16,
>> + DRM_FORMAT_NV24,
>> +};
>> +
>> +static const uint32_t formats_234[] = {
>> + DRM_FORMAT_XRGB8888,
>> + DRM_FORMAT_ARGB8888,
>> + DRM_FORMAT_RGB888,
>> + DRM_FORMAT_RGB565,
>> +};
>> +
>> +static const struct vop_win_phy win01_data = {
>> + .data_formats = formats_01,
>> + .nformats = ARRAY_SIZE(formats_01),
>> + .enable = VOP_REG(WIN0_CTRL0, 0x1, 0),
>> + .format = VOP_REG(WIN0_CTRL0, 0x7, 1),
>> + .act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0),
>> + .dsp_info = VOP_REG(WIN0_DSP_INFO, 0x1fff1fff, 0),
>> + .dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0),
>> + .yrgb_mst = VOP_REG(WIN0_YRGB_MST, 0xffffffff, 0),
>> + .uv_mst = VOP_REG(WIN0_CBR_MST, 0xffffffff, 0),
>> + .yrgb_vir = VOP_REG(WIN0_VIR, 0x3fff, 0),
>> + .uv_vir = VOP_REG(WIN0_VIR, 0x3fff, 16),
>> + .src_alpha_ctl = VOP_REG(WIN0_SRC_ALPHA_CTRL, 0xff, 0),
>> + .dst_alpha_ctl = VOP_REG(WIN0_DST_ALPHA_CTRL, 0xff, 0),
>> +};
>> +
>> +static const struct vop_win_phy win23_data = {
>> + .data_formats = formats_234,
>> + .nformats = ARRAY_SIZE(formats_234),
>> + .enable = VOP_REG(WIN2_CTRL0, 0x1, 0),
>> + .format = VOP_REG(WIN2_CTRL0, 0x7, 1),
>> + .dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0),
>> + .dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0),
>> + .yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0),
>> + .yrgb_vir = VOP_REG(WIN2_VIR0_1, 0x1fff, 0),
>> + .src_alpha_ctl = VOP_REG(WIN2_SRC_ALPHA_CTRL, 0xff, 0),
>> + .dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0),
>> +};
>> +
>> +static const struct vop_win_phy cursor_data = {
>> + .data_formats = formats_234,
>> + .nformats = ARRAY_SIZE(formats_234),
>> + .enable = VOP_REG(HWC_CTRL0, 0x1, 0),
>> + .format = VOP_REG(HWC_CTRL0, 0x7, 1),
>> + .dsp_st = VOP_REG(HWC_DSP_ST, 0x1fff1fff, 0),
>> + .yrgb_mst = VOP_REG(HWC_MST, 0xffffffff, 0),
>> +};
>> +
>> +static const struct vop_win win0 = {
>> + .base = 0,
>> + .phy = &win01_data,
>> +};
>> +
>> +static const struct vop_win win1 = {
>> + .base = 0x40,
>> + .phy = &win01_data,
>> +};
>> +
>> +static const struct vop_win win2 = {
>> + .base = 0,
>> + .phy = &win23_data,
>> +};
>> +
>> +static const struct vop_win win3 = {
>> + .base = 0x50,
>> + .phy = &win23_data,
>> +};
>> +
>> +static const struct vop_win win_cursor = {
>> + .base = 0,
>> + .phy = &cursor_data,
>> +};
>> +
>> +static const struct vop_ctrl ctrl_data = {
>> + .standby = VOP_REG(SYS_CTRL, 0x1, 22),
>> + .gate_en = VOP_REG(SYS_CTRL, 0x1, 23),
>> + .mmu_en = VOP_REG(SYS_CTRL, 0x1, 20),
>> + .rgb_en = VOP_REG(SYS_CTRL, 0x1, 12),
>> + .hdmi_en = VOP_REG(SYS_CTRL, 0x1, 13),
>> + .edp_en = VOP_REG(SYS_CTRL, 0x1, 14),
>> + .mipi_en = VOP_REG(SYS_CTRL, 0x1, 15),
>> + .dither_down = VOP_REG(DSP_CTRL1, 0xf, 1),
>> + .dither_up = VOP_REG(DSP_CTRL1, 0x1, 6),
>> + .data_blank = VOP_REG(DSP_CTRL0, 0x1, 19),
>> + .out_mode = VOP_REG(DSP_CTRL0, 0xf, 0),
>> + .pin_pol = VOP_REG(DSP_CTRL0, 0xf, 4),
>> + .htotal_pw = VOP_REG(DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
>> + .hact_st_end = VOP_REG(DSP_HACT_ST_END, 0x1fff1fff, 0),
>> + .vtotal_pw = VOP_REG(DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>> + .vact_st_end = VOP_REG(DSP_VACT_ST_END, 0x1fff1fff, 0),
>> + .hpost_st_end = VOP_REG(POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>> + .vpost_st_end = VOP_REG(POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +};
>> +
>> +static const struct vop_reg_data vop_init_reg_table[] = {
>> + {SYS_CTRL, 0x00801000},
>> + {DSP_CTRL0, 0x00000000},
>> + {WIN0_CTRL0, 0x00000080},
>> + {WIN1_CTRL0, 0x00000080},
>> +};
>> +
>> +static const struct vop_driver_data rockchip_rk3288_vop = {
>> + .init_table = vop_init_reg_table,
>> + .table_size = ARRAY_SIZE(vop_init_reg_table),
>> + .ctrl = &ctrl_data,
>> + .win[0] = &win0,
>> + .win[1] = &win1,
>> + .win[2] = &win2,
>> + .win[3] = &win3,
>> + .win[4] = &win_cursor,
>> +};
>> +
>> +static const struct of_device_id vop_driver_dt_match[] = {
>> + { .compatible = "rockchip,rk3288-vop",
>> + .data = (void *)&rockchip_rk3288_vop },
>> + {},
>> +};
>> +
>> +static inline void vop_writel(struct vop_context *ctx,
>> + uint32_t offset, uint32_t v)
>> +{
>> + writel(v, ctx->regs + offset);
> TODO: I *think* the following may be a useful optimization:
> When doing mode_set or update_plane, use writel_relaxed to write all of the
> shadowed mode/window configuration registers, and just use writel() to write
> vop_cfg_done. This will write all the configuration registers without barriers,
> but still ensure that all such registers are written before cfg_done.
>
> However, for most other register writes, you probably do want to always use
> writel().
I see, I will do it.
>> + ctx->regsbak[offset >> 2] = v;
>> +}
>> +
>> +static inline uint32_t vop_readl(struct vop_context *ctx, uint32_t offset)
>> +{
>> + return readl(ctx->regs + offset);
>> +}
>> +
>> +static inline void vop_cfg_done(struct vop_context *ctx)
>> +{
>> + writel(0x01, ctx->regs + REG_CFG_DONE);
>> +}
>> +
>> +static inline void vop_mask_write(struct vop_context *ctx,
>> + uint32_t offset, uint32_t mask, uint32_t v)
>> +{
>> + if (mask) {
>> + uint32_t cached_val = ctx->regsbak[offset >> 2];
>> +
>> + cached_val = (cached_val & ~mask) | v;
>> + writel(cached_val, ctx->regs + offset);
>> + ctx->regsbak[offset >> 2] = cached_val;
>> + }
>> +}
>> +
>> +static inline struct vop_driver_data *vop_get_driver_data(struct device *dev)
>> +{
>> + const struct of_device_id *of_id =
>> + of_match_device(vop_driver_dt_match, dev);
>> +
>> + return (struct vop_driver_data *)of_id->data;
>> +}
>> +
>> +static enum vop_data_format vop_convert_format(uint32_t format)
>> +{
>> + switch (format) {
>> + case DRM_FORMAT_XRGB8888:
>> + case DRM_FORMAT_ARGB8888:
>> + return VOP_FMT_ARGB8888;
>> + case DRM_FORMAT_RGB888:
>> + return VOP_FMT_RGB888;
>> + case DRM_FORMAT_RGB565:
>> + return VOP_FMT_RGB565;
>> + case DRM_FORMAT_NV12:
>> + return VOP_FMT_YUV420SP;
>> + case DRM_FORMAT_NV16:
>> + return VOP_FMT_YUV422SP;
>> + case DRM_FORMAT_NV24:
>> + return VOP_FMT_YUV444SP;
>> + default:
>> + DRM_ERROR("unsupport format[%08x]\n", format);
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +static bool is_alpha_support(uint32_t format)
>> +{
>> + switch (format) {
>> + case DRM_FORMAT_ARGB8888:
>> + return true;
>> + default:
>> + return false;
>> + }
>> +}
>> +
>> +/* TODO(djkurtz): move generic 'setup slave rk_iommu' code somewhere common */
>> +static int vop_iommu_init(struct vop_context *ctx)
>> +{
>> + struct device *dev = ctx->dev;
>> + struct device_node *np = dev->of_node;
>> + struct platform_device *pd;
>> + int count;
>> + int ret;
>> + struct of_phandle_args args;
>> +
>> + /* Each VOP must have exactly one iommu node, with no args */
>> + count = of_count_phandle_with_args(np, "iommus", "#iommu-cells");
>> + if (count != 1) {
>> + dev_err(dev, "of_count_phandle_with_args(%s) => %d\n",
>> + np->full_name, count);
>> + return -EINVAL;
>> + }
>> +
>> + ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
>> + &args);
>> + if (ret) {
>> + dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n",
>> + np->full_name, ret);
>> + return ret;
>> + }
>> + if (args.args_count != 0) {
>> + dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n",
>> + args.np->full_name, args.args_count);
>> + return -EINVAL;
>> + }
>> +
>> + pd = of_find_device_by_node(args.np);
>> + of_node_put(args.np);
>> + if (!pd) {
>> + dev_err(dev, "iommu %s not found\n", args.np->full_name);
>> + return -EPROBE_DEFER;
>> + }
>> +
>> + /* TODO(djkurtz): handle multiple slave iommus for a single master */
>> + dev->archdata.iommu = &pd->dev;
>> +
>> + ret = rockchip_drm_dma_attach_device(ctx->drm_dev, dev);
>> + if (ret) {
>> + dev_err(dev, "failed to attach to drm dma mapping, %d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void vop_iommu_fini(struct vop_context *ctx)
>> +{
>> + rockchip_drm_dma_detach_device(ctx->drm_dev, ctx->dev);
>> +}
>> +
>> +static int vop_clk_enable(struct vop_context *ctx)
>> +{
>> + int ret;
> if (ctx->clk_on)
> return 0;
OK.
>> +
>> + if (!ctx->clk_on) {
>> + ret = clk_enable(ctx->hclk);
>> + if (ret < 0) {
>> + dev_err(ctx->dev, "failed to enable hclk\n");
> I find it nice to print the errno, to:
>
> dev_err(ctx->dev, "failed to enable hclk, %d\n", ret);
>
>> + return ret;
>> + }
>> +
>> + ret = clk_enable(ctx->dclk);
>> + if (ret < 0) {
>> + dev_err(ctx->dev, "failed to enable dclk\n");
>> + goto err_dclk;
> goto err_disable_hclk;
>
>> + }
>> +
>> + ret = clk_enable(ctx->aclk);
>> + if (ret < 0) {
>> + dev_err(ctx->dev, "failed to enable aclk\n");
>> + goto err_aclk;;
> goto err_disable_dclk;
>
>> + }
>> + ctx->clk_on = true;
>> + }
>> +
>> + return ret;
> Hm, didn't the compiler complain about ret possibly being returned
> undefined here?
>
>> +err_aclk:
>> + clk_disable(ctx->aclk);
>> +err_dclk:
>> + clk_disable(ctx->dclk);
> err_disable_dclk:
> clk_disable(ctx->dclk);
> err_disable_hclk:
> clk_disable(ctx->hclk);
>
>> + return ret;
>> +}
>> +
>> +static void vop_clk_disable(struct vop_context *ctx)
>> +{
> if (ctx->clk_on)
> return;
>
>> + if (ctx->clk_on) {
>> + clk_disable(ctx->aclk);
>> + clk_disable(ctx->dclk);
>> + clk_disable(ctx->hclk);
>> + ctx->clk_on = false;
>> + }
>> +}
>> +
>> +static void vop_power_on(struct vop_context *ctx)
>> +{
>> + if (vop_clk_enable(ctx) < 0) {
>> + dev_err(ctx->dev, "failed to enable clks\n");
>> + return;
>> + }
>> +
>> + spin_lock(&ctx->reg_lock);
>> +
>> + VOP_CTRL_SET(ctx, standby, 0);
>> +
>> + spin_unlock(&ctx->reg_lock);
>> +}
>> +
>> +static void vop_power_off(struct vop_context *ctx)
>> +{
>> + spin_lock(&ctx->reg_lock);
>> +
>> + VOP_CTRL_SET(ctx, standby, 1);
>> +
>> + spin_unlock(&ctx->reg_lock);
>> +
>> + vop_clk_disable(ctx);
>> +}
>> +
>> +/*
>> + * @start: starting pixel in screen coordinates
>> + * @length: length of buffer "row" in pixels
>> + * @end: one pixel past end of buffer in screen coordinates
>> + * @last: width of screen, or one pixel past end of screen
>> + * @size: number of pixels of buffer to display on scree
>> + */
>> +static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
>> +{
>> + int end = start + length;
>> + int size = 0;
>> +
>> + if (start <= 0) {
>> + if (end > 0)
>> + size = min_t(unsigned, end, last);
>> + } else if (start < last) {
>> + size = min_t(unsigned, last - start, length);
>> + }
>> +
>> + return size;
>> +}
>> +
>> +static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>> + struct drm_framebuffer *fb, int crtc_x,
>> + int crtc_y, unsigned int crtc_w,
>> + unsigned int crtc_h, uint32_t src_x,
>> + uint32_t src_y, uint32_t src_w, uint32_t src_h)
>> +{
>> + struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
>> + const struct vop_win *win = rockchip_plane->win;
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> + struct drm_gem_object *obj;
>> + struct rockchip_gem_object *rk_obj;
>> + unsigned long offset;
>> + unsigned int actual_w;
>> + unsigned int actual_h;
>> + unsigned int dsp_stx;
>> + unsigned int dsp_sty;
>> + unsigned int y_vir_stride;
>> + dma_addr_t yrgb_mst;
>> + enum vop_data_format format;
>> + uint32_t val;
>> + bool is_alpha;
>> +
>> + src_w >>= 16;
>> + src_h >>= 16;
>> + src_x >>= 16;
>> + src_y >>= 16;
>> +
>> + if (crtc_w != src_w || crtc_h != src_h)
>> + return -EINVAL;
> Can we use drm_plane_helper_check_update() here instead of doing our own checks?
OK.
>> +
>> + actual_w = rockchip_plane_get_size(crtc_x,
>> + crtc_w, crtc->mode.hdisplay);
>> + actual_h = rockchip_plane_get_size(crtc_y,
>> + crtc_h, crtc->mode.vdisplay);
>> + if (!actual_w || !actual_h)
>> + return -EINVAL;
>> +
>> + if (crtc_x < 0) {
>> + if (actual_w)
>> + src_x -= crtc_x;
>> + crtc_x = 0;
>> + }
>> +
>> + if (crtc_y < 0) {
>> + if (actual_h)
>> + src_y -= crtc_y;
>> + crtc_y = 0;
>> + }
>> +
>> + is_alpha = is_alpha_support(fb->pixel_format);
>> + format = vop_convert_format(fb->pixel_format);
>> + if (format < 0)
>> + return format;
>> +
>> + obj = rockchip_fb_get_gem_obj(fb, 0);
>> + if (!obj) {
>> + DRM_ERROR("fail to get rockchip gem object from framebuffer\n");
>> + return -EINVAL;
>> + }
>> +
>> + rk_obj = to_rockchip_obj(obj);
>> +
>> + yrgb_mst = rk_obj->dma_addr;
>> +
>> + dsp_stx = crtc_x + crtc->mode.htotal - crtc->mode.hsync_start;
>> + dsp_sty = crtc_y + crtc->mode.vtotal - crtc->mode.vsync_start;
>> +
>> + offset = src_x * (fb->bits_per_pixel >> 3);
>> + offset += src_y * fb->pitches[0];
>> +
>> + y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
>> +
>> + spin_lock(&ctx->reg_lock);
>> +
>> + VOP_WIN_SET(ctx, win, format, format);
>> + VOP_WIN_SET(ctx, win, yrgb_vir, y_vir_stride);
>> + yrgb_mst += offset;
>> + VOP_WIN_SET(ctx, win, yrgb_mst, yrgb_mst);
>> + val = ((actual_h - 1) & 0xffff) << 16;
>> + val |= (actual_w - 1) & 0xffff;
>> + VOP_WIN_SET(ctx, win, act_info, val);
>> + VOP_WIN_SET(ctx, win, dsp_info, val);
>> + val = ((dsp_sty - 1) & 0xffff) << 16;
>> + val |= (dsp_stx - 1) & 0xffff;
>> + VOP_WIN_SET(ctx, win, dsp_st, val);
>> +
> It looks a bit funny to me, but according to the TRM I am looking at:
> act mask is 0x1fff
> dsp_info is 0x0fff
> dsp_st is 0x1fff
>
> But these would be better as macros:
>
> #define ACT_INFO_VAL(x) ((x - 1) & 0x1fff)
> #define DSP_INFO_VAL(x) ((x - 1) & 0x0fff)
> #define DSP_ST_VAL(x) ((x - 1) & 0x1fff)
>
> #define ACT_INFO(w, h) ((ACT_INFO_VAL(h) << 16) | (ACT_INFO_VAL(w))
> #define DSP_INFO(w, h) ((DSP_INFO_VAL(h) << 16) | (DSP_INFO_VAL(w))
> #define DSP_INFO(x, y) ((DSP_ST_VAL(y) << 16) | (DSP_ST_VAL(x))
At here, I only want to make sure width at low 16bit, height at high 16bit.
and you said act mask is 0x1fff, this mask would do by VOP_WIN_SET.
VOP_WIN_SET use vop_mask_write, and the mask define from win_data
static const struct vop_win_phy win01_data = {
...
.act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0),
}
>> + if (is_alpha) {
>> + VOP_WIN_SET(ctx, win, dst_alpha_ctl,
>> + DST_FACTOR_M0(ALPHA_SRC_INVERSE));
>> + val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
>> + SRC_ALPHA_M0(ALPHA_STRAIGHT) |
>> + SRC_BLEND_M0(ALPHA_PER_PIX) |
>> + SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
>> + SRC_FACTOR_M0(ALPHA_ONE);
>> + VOP_WIN_SET(ctx, win, src_alpha_ctl, val);
>> + } else {
>> + VOP_WIN_SET(ctx, win, src_alpha_ctl, SRC_ALPHA_EN(0));
>> + }
>> +
>> + VOP_WIN_SET(ctx, win, enable, 1);
>> +
>> + spin_unlock(&ctx->reg_lock);
>> +
>> + mutex_lock(&ctx->vsync_mutex);
>> +
>> + /*
>> + * Because the buffer set to vop take effect at frame start time,
>> + * we need make sure old buffer is not in use before we release
>> + * it.
>> + * reference the framebuffer, and unference it when it swap out of vop.
>> + */
>> + if (fb != rockchip_plane->front_fb) {
>> + drm_framebuffer_reference(fb);
>> + if (rockchip_plane->pending_fb)
>> + drm_framebuffer_unreference(rockchip_plane->pending_fb);
>> + rockchip_plane->pending_fb = fb;
>> + rockchip_plane->pending_yrgb_mst = yrgb_mst;
>> + ctx->vsync_work_pending = true;
>> + }
>> + rockchip_plane->enabled = true;
>> +
>> + mutex_unlock(&ctx->vsync_mutex);
>> +
>> + spin_lock(&ctx->reg_lock);
>> + vop_cfg_done(ctx);
>> + spin_unlock(&ctx->reg_lock);
>> +
>> + return 0;
>> +}
>> +
>> +#define rockchip_update_plane_helper(plane, crtc, fb, x, y) \
>> + rockchip_update_plane(plane, crtc, fb, 0, 0, \
>> + (fb)->width, (fb)->height, \
>> + (x) << 16, (y) << 16, \
>> + (fb)->width << 16, (fb)->height << 16)
> (1) No. This is using the framebuffer size. We should be using the
> crtc's mode's
> dimensions to display the primary plane.
> (2) Also, please make this an inline function named
> rockchip_update_primary_plane()
OK, I will do it.
>> +
>> +static int rockchip_disable_plane(struct drm_plane *plane)
>> +{
>> + struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
>> + const struct vop_win *win = rockchip_plane->win;
>> + struct vop_context *ctx;
>> +
>> + if (!plane->crtc)
>> + return 0;
>> +
>> + ctx = to_vop_ctx(plane->crtc);
>> + spin_lock(&ctx->reg_lock);
>> +
>> + VOP_WIN_SET(ctx, win, enable, 0);
>> + vop_cfg_done(ctx);
>> +
>> + spin_unlock(&ctx->reg_lock);
>> +
>> + mutex_lock(&ctx->vsync_mutex);
>> +
>> + /*
>> + * clear the pending framebuffer and set vsync_work_pending true,
>> + * so that the framebuffer will unref at the next vblank.
>> + */
>> + if (rockchip_plane->pending_fb) {
>> + drm_framebuffer_unreference(rockchip_plane->pending_fb);
>> + rockchip_plane->pending_fb = NULL;
>> + }
>> +
>> + rockchip_plane->enabled = false;
>> + ctx->vsync_work_pending = true;
>> +
>> + mutex_unlock(&ctx->vsync_mutex);
>> +
>> + return 0;
>> +}
>> +
>> +static void rockchip_plane_destroy(struct drm_plane *plane)
>> +{
>> + struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
>> + struct vop_context *ctx = to_vop_ctx(plane->crtc);
>> +
>> + rockchip_disable_plane(plane);
>> + drm_plane_cleanup(plane);
>> + ctx->win_mask &= ~(1 << rockchip_plane->id);
>> + kfree(rockchip_plane);
>> +}
>> +
>> +static const struct drm_plane_funcs rockchip_plane_funcs = {
>> + .update_plane = rockchip_update_plane,
>> + .disable_plane = rockchip_disable_plane,
>> + .destroy = rockchip_plane_destroy,
>> +};
>> +
>> +static struct drm_plane *rockchip_plane_init(struct vop_context *ctx,
>> + unsigned long possible_crtcs,
>> + enum drm_plane_type type,
>> + int index)
>> +{
>> + struct rockchip_plane *rockchip_plane;
>> + struct vop_driver_data *vop_data = ctx->data;
>> + const struct vop_win *win;
>> + int err;
>> +
>> + if (index >= VOP_MAX_WIN_SUPPORT)
>> + return ERR_PTR(-EINVAL);
>> +
>> + rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
>> + if (!rockchip_plane)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + win = vop_data->win[index];
>> + ctx->win_mask |= (1 << index);
>> + rockchip_plane->id = index;
>> + rockchip_plane->win = win;
>> +
>> + err = drm_universal_plane_init(ctx->drm_dev, &rockchip_plane->base,
>> + possible_crtcs, &rockchip_plane_funcs,
>> + win->phy->data_formats,
>> + win->phy->nformats, type);
>> + if (err) {
>> + DRM_ERROR("failed to initialize plane\n");
>> + kfree(rockchip_plane);
>> + return ERR_PTR(err);
>> + }
>> +
>> + return &rockchip_plane->base;
>> +}
>> +
>> +int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc, int connector_type)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> +
>> + if (ctx->dpms != DRM_MODE_DPMS_ON)
>> + return -EPERM;
>> +
>> + switch (connector_type) {
>> + case DRM_MODE_CONNECTOR_LVDS:
>> + VOP_CTRL_SET(ctx, rgb_en, 1);
>> + VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUT_MODE_P888);
>> + break;
>> + case DRM_MODE_CONNECTOR_eDP:
>> + VOP_CTRL_SET(ctx, edp_en, 1);
>> + VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUT_MODE_AAAA);
>> + break;
>> + case DRM_MODE_CONNECTOR_HDMIA:
>> + VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUT_MODE_AAAA);
>> + VOP_CTRL_SET(ctx, hdmi_en, 1);
>> + break;
>> + default:
>> + DRM_ERROR("unsupport connector_type[%d]\n", connector_type);
>> + return -EINVAL;
>> + };
>> +
>> + return 0;
>> +}
>> +
>> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(rockchip_drm_find_crtc(dev, pipe));
>> + unsigned long flags;
>> +
>> + if (ctx->dpms != DRM_MODE_DPMS_ON)
>> + return -EPERM;
>> +
>> + spin_lock_irqsave(&ctx->irq_lock, flags);
>> +
>> + vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
>> + LINE_FLAG_INTR_EN(1));
>> +
>> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
>> +
>> + return 0;
>> +}
>> +
>> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(rockchip_drm_find_crtc(dev, pipe));
>> + unsigned long flags;
>> +
>> + if (ctx->dpms != DRM_MODE_DPMS_ON)
>> + return;
>> + spin_lock_irqsave(&ctx->irq_lock, flags);
>> + vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
>> + LINE_FLAG_INTR_EN(0));
>> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
>> +}
>> +
>> +static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> +
>> + DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
>> +
>> + if (ctx->dpms == mode) {
>> + DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
>> + return;
>> + }
>> + if (mode > DRM_MODE_DPMS_ON) {
>> + /* wait for the completion of page flip. */
>> + if (!wait_event_timeout(ctx->wait_vsync_queue,
>> + !atomic_read(&ctx->wait_vsync_event),
>> + HZ/20))
>> + DRM_DEBUG_KMS("vblank wait timed out.\n");
>> + drm_vblank_off(crtc->dev, ctx->pipe);
>> + }
>> +
>> + switch (mode) {
>> + case DRM_MODE_DPMS_ON:
>> + vop_power_on(ctx);
>> + break;
>> + case DRM_MODE_DPMS_STANDBY:
>> + case DRM_MODE_DPMS_SUSPEND:
>> + case DRM_MODE_DPMS_OFF:
>> + vop_power_off(ctx);
>> + break;
>> + default:
>> + DRM_DEBUG_KMS("unspecified mode %d\n", mode);
>> + break;
>> + }
>> +
>> + ctx->dpms = mode;
>> +}
>> +
>> +static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
>> +{
>> + rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
>> +}
>> +
>> +static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
>> + const struct drm_display_mode *mode,
>> + struct drm_display_mode *adjusted_mode)
>> +{
>> + if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
>> + return false;
>> +
>> + return true;
>> +}
>> +
>> +static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
>> + struct drm_framebuffer *old_fb);
>> +
>> +static int rockchip_drm_crtc_mode_set(struct drm_crtc *crtc,
>> + struct drm_display_mode *mode,
>> + struct drm_display_mode *adjusted_mode,
>> + int x, int y,
>> + struct drm_framebuffer *fb)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> + u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
>> + u16 hdisplay = adjusted_mode->hdisplay;
>> + u16 htotal = adjusted_mode->htotal;
>> + u16 hact_st = adjusted_mode->htotal - adjusted_mode->hsync_start;
>> + u16 hact_end = hact_st + hdisplay;
>> + u16 vdisplay = adjusted_mode->vdisplay;
>> + u16 vtotal = adjusted_mode->vtotal;
>> + u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
>> + u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
>> + u16 vact_end = vact_st + vdisplay;
>> + unsigned long flags;
>> + int ret;
>> + uint32_t val;
>> +
>> + clk_disable(ctx->dclk);
>> +
>> + ret = rockchip_drm_crtc_mode_set_base(crtc, x, y, fb);
>> + if (ret)
>> + return ret;
>> +
>> + val = 0x8;
>> + val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0;
>> + val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? (1 << 1) : 0;
>> + VOP_CTRL_SET(ctx, pin_pol, val);
>> +
>> + VOP_CTRL_SET(ctx, htotal_pw, (htotal << 16) | hsync_len);
>> + val = hact_st << 16;
>> + val |= hact_end;
>> + VOP_CTRL_SET(ctx, hact_st_end, val);
>> + VOP_CTRL_SET(ctx, hpost_st_end, val);
>> +
>> + VOP_CTRL_SET(ctx, vtotal_pw, (vtotal << 16) | vsync_len);
>> + val = vact_st << 16;
>> + val |= vact_end;
>> + VOP_CTRL_SET(ctx, vact_st_end, val);
>> + VOP_CTRL_SET(ctx, vpost_st_end, val);
>> +
>> + spin_lock_irqsave(&ctx->irq_lock, flags);
>> +
>> + vop_mask_write(ctx, INTR_CTRL0, DSP_LINE_NUM_MASK,
>> + DSP_LINE_NUM(vact_end));
>> +
>> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
>> +
>> + /*
>> + * do dclk_reset, let all config take affect, so new clk will safely
>> + * run in correct frame.
>> + */
>> + reset_control_assert(ctx->dclk_rst);
>> + usleep_range(10, 20);
>> + reset_control_deassert(ctx->dclk_rst);
>> +
>> + clk_set_rate(ctx->dclk, adjusted_mode->clock * 1000);
>> + clk_enable(ctx->dclk);
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
>> + struct drm_framebuffer *old_fb)
>> +{
>> + int ret;
>> +
>> + ret = rockchip_update_plane_helper(crtc->primary, crtc,
>> + crtc->primary->fb, x, y);
>> + if (ret < 0) {
>> + DRM_ERROR("fail to update plane\n");
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
>> +{
>> + /* just do dummy now */
>> +}
>> +
>> +static const struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
>> + .dpms = rockchip_drm_crtc_dpms,
>> + .prepare = rockchip_drm_crtc_prepare,
>> + .mode_fixup = rockchip_drm_crtc_mode_fixup,
>> + .mode_set = rockchip_drm_crtc_mode_set,
>> + .mode_set_base = rockchip_drm_crtc_mode_set_base,
>> + .commit = rockchip_drm_crtc_commit,
>> +};
>> +
>> +static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
>> + struct drm_framebuffer *fb,
>> + struct drm_pending_vblank_event *event,
>> + uint32_t page_flip_flags)
>> +{
>> + struct drm_device *dev = crtc->dev;
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> + struct drm_framebuffer *old_fb = crtc->primary->fb;
>> + int ret;
>> +
>> + /* when the page flip is requested, crtc's dpms should be on */
>> + if (ctx->dpms > DRM_MODE_DPMS_ON) {
>> + DRM_DEBUG("failed page flip request at dpms[%d].\n", ctx->dpms);
>> + return 0;
>> + }
>> +
>> + ret = drm_vblank_get(dev, ctx->pipe);
>> + if (ret) {
>> + DRM_DEBUG("failed to acquire vblank counter\n");
>> + return ret;
>> + }
>> +
>> + spin_lock_irq(&dev->event_lock);
>> + if (ctx->event) {
>> + spin_unlock_irq(&dev->event_lock);
>> + DRM_ERROR("already pending flip!\n");
>> + return -EBUSY;
>> + }
>> + ctx->event = event;
>> + atomic_set(&ctx->wait_vsync_event, 1);
>> + spin_unlock_irq(&dev->event_lock);
>> +
>> + crtc->primary->fb = fb;
>> +
>> + ret = rockchip_update_plane_helper(crtc->primary, crtc, fb,
>> + crtc->x, crtc->y);
>> + if (ret) {
>> + crtc->primary->fb = old_fb;
>> +
>> + spin_lock_irq(&dev->event_lock);
>> + drm_vblank_put(dev, ctx->pipe);
>> + atomic_set(&ctx->wait_vsync_event, 0);
>> + ctx->event = NULL;
>> + spin_unlock_irq(&dev->event_lock);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
>> +{
>> + struct rockchip_drm_private *dev_priv = dev->dev_private;
>> + struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
>> + struct vop_context *ctx;
>> + unsigned long flags;
>> +
>> + ctx = to_vop_ctx(drm_crtc);
>> +
>> + spin_lock_irqsave(&dev->event_lock, flags);
>> +
>> + if (ctx->event) {
>> + drm_send_vblank_event(dev, -1, ctx->event);
>> + drm_vblank_put(dev, pipe);
>> + atomic_set(&ctx->wait_vsync_event, 0);
>> + wake_up(&ctx->wait_vsync_queue);
>> + ctx->event = NULL;
>> + }
>> +
>> + spin_unlock_irqrestore(&dev->event_lock, flags);
>> +}
>> +
>> +static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
>> +{
>> + struct vop_context *ctx = to_vop_ctx(crtc);
>> + struct rockchip_drm_private *private = crtc->dev->dev_private;
>> +
>> + private->crtc[ctx->pipe] = NULL;
>> + drm_crtc_cleanup(crtc);
>> +}
>> +
>> +static const struct drm_crtc_funcs rockchip_crtc_funcs = {
>> + .set_config = drm_crtc_helper_set_config,
>> + .page_flip = rockchip_drm_crtc_page_flip,
>> + .destroy = rockchip_drm_crtc_destroy,
>> +};
>> +
>> +static void rockchip_vsync_worker(struct work_struct *work)
>> +{
>> + struct vop_context *ctx = container_of(work, struct vop_context,
>> + vsync_work);
>> + struct drm_device *drm = ctx->drm_dev;
>> + struct rockchip_drm_private *dev_priv = drm->dev_private;
>> + struct drm_crtc *crtc = dev_priv->crtc[ctx->pipe];
>> + struct rockchip_plane *rockchip_plane;
>> + struct drm_plane *plane;
>> + uint32_t yrgb_mst;
>> +
>> + mutex_lock(&ctx->vsync_mutex);
>> +
>> + ctx->vsync_work_pending = false;
>> +
>> + list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
>> + rockchip_plane = to_rockchip_plane(plane);
>> +
>> + if (to_vop_ctx(plane->crtc) != ctx)
>> + continue;
>> + if (rockchip_plane->enabled && !rockchip_plane->pending_fb)
>> + continue;
>> + if (!rockchip_plane->enabled && !rockchip_plane->front_fb)
>> + continue;
>> + /*
>> + * make sure the yrgb_mst take effect, so that
>> + * we can unreference the old framebuffer.
>> + */
>> + yrgb_mst = VOP_WIN_GET_YRGBADDR(ctx, rockchip_plane->win);
>> + if (rockchip_plane->pending_yrgb_mst != yrgb_mst) {
>> + /*
>> + * some plane no complete, unref at next vblank
>> + */
>> + ctx->vsync_work_pending = true;
>> + continue;
>> + }
>> +
>> + /*
>> + * drm_framebuffer_unreference maybe call iommu unmap,
>> + * and iommu not allow unmap buffer at irq context,
>> + * so we do drm_framebuffer_unreference at queue_work.
>> + */
>> + if (rockchip_plane->front_fb)
>> + drm_framebuffer_unreference(rockchip_plane->front_fb);
>> +
>> + rockchip_plane->front_fb = rockchip_plane->pending_fb;
>> + rockchip_plane->pending_fb = NULL;
>> +
>> + /*
>> + * if primary plane flip complete, sending the event to
>> + * userspace
>> + */
>> + if (&rockchip_plane->base == crtc->primary)
>> + rockchip_drm_crtc_finish_pageflip(ctx->drm_dev,
>> + ctx->pipe);
>> + }
>> +
>> + mutex_unlock(&ctx->vsync_mutex);
>> +}
>> +
>> +static irqreturn_t rockchip_vop_isr(int irq, void *data)
>> +{
>> + struct vop_context *ctx = data;
>> + uint32_t intr0_reg;
>> + unsigned long flags;
>> +
>> + intr0_reg = vop_readl(ctx, INTR_CTRL0);
>> + if (intr0_reg & LINE_FLAG_INTR) {
>> + spin_lock_irqsave(&ctx->irq_lock, flags);
>> + vop_writel(ctx, INTR_CTRL0, intr0_reg | LINE_FLAG_INTR_CLR);
>> + spin_unlock_irqrestore(&ctx->irq_lock, flags);
>> + } else {
>> + return IRQ_NONE;
>> + }
>> +
>> + drm_handle_vblank(ctx->drm_dev, ctx->pipe);
>> + if (ctx->vsync_work_pending)
>> + queue_work(ctx->vsync_wq, &ctx->vsync_work);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int vop_create_crtc(struct vop_context *ctx)
>> +{
>> + struct device *dev = ctx->dev;
>> + struct drm_device *drm_dev = ctx->drm_dev;
>> + struct drm_plane *primary, *cursor;
>> + unsigned long possible_crtcs;
>> + struct drm_crtc *crtc;
>> + int ret;
>> + int nr;
>> +
>> + ctx->win_mask = 0;
>> + crtc = &ctx->crtc;
>> +
>> + ret = rockchip_drm_add_crtc(drm_dev, crtc, dev->of_node);
>> + if (ret < 0)
>> + return ret;
>> + ctx->pipe = ret;
>> +
>> + possible_crtcs = (1 << ctx->pipe);
>> +
>> + for (nr = 0; nr < VOP_MAX_WIN_SUPPORT; nr++) {
>> + if (nr == VOP_DEFAULT_PRIMARY) {
>> + primary = rockchip_plane_init(ctx, possible_crtcs,
>> + DRM_PLANE_TYPE_PRIMARY,
>> + VOP_DEFAULT_PRIMARY);
>> + if (IS_ERR(primary)) {
>> + DRM_ERROR("fail to init primary plane\n");
>> + return PTR_ERR(primary);
>> + }
>> + } else if (nr == VOP_DEFAULT_CURSOR) {
>> + cursor = rockchip_plane_init(ctx, possible_crtcs,
>> + DRM_PLANE_TYPE_CURSOR,
>> + VOP_DEFAULT_CURSOR);
>> + if (IS_ERR(cursor)) {
>> + DRM_ERROR("fail to init cursor plane\n");
>> + return PTR_ERR(cursor);
>> + }
>> + } else {
>> + struct drm_plane *plane;
>> +
>> + plane = rockchip_plane_init(ctx, possible_crtcs,
>> + DRM_PLANE_TYPE_OVERLAY,
>> + nr);
>> + if (IS_ERR(plane)) {
>> + DRM_ERROR("fail to init overlay plane\n");
>> + return PTR_ERR(plane);
>> + }
>> + }
>> + }
>> +
>> + drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
>> + &rockchip_crtc_funcs);
>> + drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_vop_initial(struct vop_context *ctx)
>> +{
>> + struct vop_driver_data *vop_data = ctx->data;
>> + const struct vop_reg_data *init_table = vop_data->init_table;
>> + struct reset_control *ahb_rst;
>> + int i, ret;
>> +
>> + ctx->hclk = devm_clk_get(ctx->dev, "hclk_vop");
>> + if (IS_ERR(ctx->hclk)) {
>> + dev_err(ctx->dev, "failed to get hclk source\n");
>> + return PTR_ERR(ctx->hclk);
>> + }
>> + ctx->aclk = devm_clk_get(ctx->dev, "aclk_vop");
>> + if (IS_ERR(ctx->aclk)) {
>> + dev_err(ctx->dev, "failed to get aclk source\n");
>> + return PTR_ERR(ctx->aclk);
>> + }
>> + ctx->dclk = devm_clk_get(ctx->dev, "dclk_vop");
>> + if (IS_ERR(ctx->dclk)) {
>> + dev_err(ctx->dev, "failed to get dclk source\n");
>> + return PTR_ERR(ctx->dclk);
>> + }
>> +
>> + ret = clk_prepare(ctx->hclk);
>> + if (ret < 0) {
>> + dev_err(ctx->dev, "failed to prepare hclk\n");
>> + return ret;
>> + }
>> +
>> + ret = clk_prepare(ctx->dclk);
>> + if (ret < 0) {
>> + dev_err(ctx->dev, "failed to prepare dclk\n");
>> + goto err_prepare_hclk;
> goto err_unprepare_hclk;
>
>> + }
>> +
>> + ret = clk_prepare(ctx->aclk);
>> + if (ret < 0) {
>> + dev_err(ctx->dev, "failed to prepare aclk\n");
>> + goto err_prepare_dclk;
> goto err_unprepare_dclk;
>
>> + }
>> +
>> + ret = vop_clk_enable(ctx);
>> + if (ret < 0)
>> + goto err_prepare_aclk;
> goto err_unprepare_aclk;
>
>> +
>> + /*
>> + * do hclk_reset, reset all vop registers.
>> + */
>> + ahb_rst = devm_reset_control_get(ctx->dev, "ahb");
>> + if (IS_ERR(ahb_rst)) {
>> + dev_err(ctx->dev, "failed to get ahb reset\n");
>> + ret = PTR_ERR(ahb_rst);
>> + goto err_diable_clk;
>> + }
>> + reset_control_assert(ahb_rst);
>> + usleep_range(10, 20);
>> + reset_control_deassert(ahb_rst);
>> +
>> + memcpy(ctx->regsbak, ctx->regs, ctx->len);
>> +
>> + for (i = 0; i < vop_data->table_size; i++)
>> + vop_writel(ctx, init_table[i].offset, init_table[i].value);
>> +
>> + for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++)
>> + VOP_WIN_SET(ctx, vop_data->win[i], enable, 0);
>> +
>> + vop_cfg_done(ctx);
>> +
>> + /*
>> + * do dclk_reset, let all win config take affect, and then we can enable
>> + * iommu safe.
>> + */
>> + ctx->dclk_rst = devm_reset_control_get(ctx->dev, "dclk");
>> + if (IS_ERR(ctx->dclk_rst)) {
>> + dev_err(ctx->dev, "failed to get dclk reset\n");
>> + ret = PTR_ERR(ctx->dclk_rst);
>> + goto err_diable_clk;
> goto err_disable_clk;
>
>> + }
>> + reset_control_assert(ctx->dclk_rst);
>> + usleep_range(10, 20);
>> + reset_control_deassert(ctx->dclk_rst);
>> +
>> + ctx->dpms = DRM_MODE_DPMS_ON;
> The reset at probe time is probably a good idea.
>
> However, this leaves the VOP enabled and clocking at... but what will it be
> displaying, and what mode will it use?
>
> Perhaps we should delay enabling clocks / dpms until the first mode set /
> dpms(DPMS_ON) request?
OK, let me try it.
>> +
>> + return 0;
>> +
>> +err_prepare_aclk:
>> + clk_unprepare(ctx->aclk);
>> +err_prepare_dclk:
>> + clk_unprepare(ctx->dclk);
>> +err_prepare_hclk:
>> + clk_unprepare(ctx->hclk);
>> +err_diable_clk:
>> + vop_clk_disable(ctx);
>> + return ret;
> err_disable_clk:
> vop_clk_disable(ctx);
> err_unprepare_aclk:
> clk_unprepare(ctx->aclk);
> err_unprepare_dclk:
> clk_unprepare(ctx->dclk);
> err_unprepare_hclk:
> clk_unprepare(ctx->hclk);
> return ret;
>
> That's it for now!
>
> -djk
Thanks for you review:-) .
>
>> +}
>> +
>> +static int vop_bind(struct device *dev, struct device *master, void *data)
>> +{
>> + struct platform_device *pdev = to_platform_device(dev);
>> + struct vop_driver_data *vop_data = vop_get_driver_data(dev);
>> + struct drm_device *drm_dev = data;
>> + struct vop_context *ctx;
>> + struct resource *res;
>> + int ret;
>> +
>> + if (!vop_data)
>> + return -ENODEV;
>> +
>> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>> + if (!ctx)
>> + return -ENOMEM;
>> +
>> + ctx->dev = dev;
>> + ctx->data = vop_data;
>> + ctx->drm_dev = drm_dev;
>> + dev_set_drvdata(dev, ctx);
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + ctx->len = resource_size(res);
>> + ctx->regs = devm_ioremap_resource(dev, res);
>> + if (IS_ERR(ctx->regs))
>> + return PTR_ERR(ctx->regs);
>> +
>> + ctx->regsbak = devm_kzalloc(dev, ctx->len, GFP_KERNEL);
>> + if (!ctx->regsbak)
>> + return -ENOMEM;
>> +
>> + ret = rockchip_vop_initial(ctx);
>> + if (ret < 0) {
>> + dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ctx->irq = platform_get_irq(pdev, 0);
>> + if (ctx->irq < 0) {
>> + dev_err(dev, "cannot find irq for vop\n");
>> + return ctx->irq;
>> + }
>> +
>> + spin_lock_init(&ctx->reg_lock);
>> + spin_lock_init(&ctx->irq_lock);
>> +
>> + init_waitqueue_head(&ctx->wait_vsync_queue);
>> + atomic_set(&ctx->wait_vsync_event, 0);
>> +
>> + ret = vop_iommu_init(ctx);
>> + if (ret) {
>> + DRM_ERROR("Failed to setup iommu, %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ctx->vsync_wq = create_singlethread_workqueue("vsync");
>> + if (!ctx->vsync_wq) {
>> + dev_err(dev, "failed to create workqueue\n");
>> + return -EINVAL;
>> + }
>> + INIT_WORK(&ctx->vsync_work, rockchip_vsync_worker);
>> +
>> + mutex_init(&ctx->vsync_mutex);
>> + pm_runtime_enable(&pdev->dev);
>> +
>> + ret = devm_request_irq(dev, ctx->irq, rockchip_vop_isr,
>> + IRQF_SHARED, dev_name(dev), ctx);
>> + if (ret) {
>> + dev_err(dev, "cannot requeset irq%d - err %d\n", ctx->irq, ret);
>> + return ret;
>> + }
>> +
>> + return vop_create_crtc(ctx);
>> +}
>> +
>> +static void vop_unbind(struct device *dev, struct device *master,
>> + void *data)
>> +{
>> + struct drm_device *drm_dev = data;
>> + struct vop_context *ctx = dev_get_drvdata(dev);
>> + struct drm_crtc *crtc = &ctx->crtc;
>> +
>> + drm_crtc_cleanup(crtc);
>> + pm_runtime_disable(dev);
>> + rockchip_drm_remove_crtc(drm_dev, ctx->pipe);
>> +
>> + vop_iommu_fini(ctx);
>> +}
>> +
>> +static const struct component_ops vop_component_ops = {
>> + .bind = vop_bind,
>> + .unbind = vop_unbind,
>> +};
>> +
>> +static int vop_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct vop_context *ctx;
>> +
>> + if (!dev->of_node) {
>> + dev_err(dev, "can't find vop devices\n");
>> + return -ENODEV;
>> + }
>> +
>> + platform_set_drvdata(pdev, ctx);
>> +
>> + return component_add(dev, &vop_component_ops);
>> +}
>> +
>> +static int vop_remove(struct platform_device *pdev)
>> +{
>> + component_del(&pdev->dev, &vop_component_ops);
>> +
>> + return 0;
>> +}
>> +
>> +struct platform_driver rockchip_vop_platform_driver = {
>> + .probe = vop_probe,
>> + .remove = vop_remove,
>> + .driver = {
>> + .name = "rockchip-vop",
>> + .owner = THIS_MODULE,
>> + .of_match_table = of_match_ptr(vop_driver_dt_match),
>> + },
>> +};
>> +
>> +module_platform_driver(rockchip_vop_platform_driver);
>> +
>> +MODULE_AUTHOR("Mark Yao <mark.yao@rock-chips.com>");
>> +MODULE_DESCRIPTION("ROCKCHIP VOP Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> new file mode 100644
>> index 0000000..d2a04f0
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> @@ -0,0 +1,196 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author:Mark Yao <mark.yao@rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#ifndef _ROCKCHIP_DRM_VOP_H
>> +#define _ROCKCHIP_DRM_VOP_H
>> +
>> +/* register definition */
>> +#define REG_CFG_DONE 0x0000
>> +#define VERSION_INFO 0x0004
>> +#define SYS_CTRL 0x0008
>> +#define SYS_CTRL1 0x000c
>> +#define DSP_CTRL0 0x0010
>> +#define DSP_CTRL1 0x0014
>> +#define DSP_BG 0x0018
>> +#define MCU_CTRL 0x001c
>> +#define INTR_CTRL0 0x0020
>> +#define INTR_CTRL1 0x0024
>> +#define WIN0_CTRL0 0x0030
>> +#define WIN0_CTRL1 0x0034
>> +#define WIN0_COLOR_KEY 0x0038
>> +#define WIN0_VIR 0x003c
>> +#define WIN0_YRGB_MST 0x0040
>> +#define WIN0_CBR_MST 0x0044
>> +#define WIN0_ACT_INFO 0x0048
>> +#define WIN0_DSP_INFO 0x004c
>> +#define WIN0_DSP_ST 0x0050
>> +#define WIN0_SCL_FACTOR_YRGB 0x0054
>> +#define WIN0_SCL_FACTOR_CBR 0x0058
>> +#define WIN0_SCL_OFFSET 0x005c
>> +#define WIN0_SRC_ALPHA_CTRL 0x0060
>> +#define WIN0_DST_ALPHA_CTRL 0x0064
>> +#define WIN0_FADING_CTRL 0x0068
>> +/* win1 register */
>> +#define WIN1_CTRL0 0x0070
>> +#define WIN1_CTRL1 0x0074
>> +#define WIN1_COLOR_KEY 0x0078
>> +#define WIN1_VIR 0x007c
>> +#define WIN1_YRGB_MST 0x0080
>> +#define WIN1_CBR_MST 0x0084
>> +#define WIN1_ACT_INFO 0x0088
>> +#define WIN1_DSP_INFO 0x008c
>> +#define WIN1_DSP_ST 0x0090
>> +#define WIN1_SCL_FACTOR_YRGB 0x0094
>> +#define WIN1_SCL_FACTOR_CBR 0x0098
>> +#define WIN1_SCL_OFFSET 0x009c
>> +#define WIN1_SRC_ALPHA_CTRL 0x00a0
>> +#define WIN1_DST_ALPHA_CTRL 0x00a4
>> +#define WIN1_FADING_CTRL 0x00a8
>> +/* win2 register */
>> +#define WIN2_CTRL0 0x00b0
>> +#define WIN2_CTRL1 0x00b4
>> +#define WIN2_VIR0_1 0x00b8
>> +#define WIN2_VIR2_3 0x00bc
>> +#define WIN2_MST0 0x00c0
>> +#define WIN2_DSP_INFO0 0x00c4
>> +#define WIN2_DSP_ST0 0x00c8
>> +#define WIN2_COLOR_KEY 0x00cc
>> +#define WIN2_MST1 0x00d0
>> +#define WIN2_DSP_INFO1 0x00d4
>> +#define WIN2_DSP_ST1 0x00d8
>> +#define WIN2_SRC_ALPHA_CTRL 0x00dc
>> +#define WIN2_MST2 0x00e0
>> +#define WIN2_DSP_INFO2 0x00e4
>> +#define WIN2_DSP_ST2 0x00e8
>> +#define WIN2_DST_ALPHA_CTRL 0x00ec
>> +#define WIN2_MST3 0x00f0
>> +#define WIN2_DSP_INFO3 0x00f4
>> +#define WIN2_DSP_ST3 0x00f8
>> +#define WIN2_FADING_CTRL 0x00fc
>> +/* win3 register */
>> +#define WIN3_CTRL0 0x0100
>> +#define WIN3_CTRL1 0x0104
>> +#define WIN3_VIR0_1 0x0108
>> +#define WIN3_VIR2_3 0x010c
>> +#define WIN3_MST0 0x0110
>> +#define WIN3_DSP_INFO0 0x0114
>> +#define WIN3_DSP_ST0 0x0118
>> +#define WIN3_COLOR_KEY 0x011c
>> +#define WIN3_MST1 0x0120
>> +#define WIN3_DSP_INFO1 0x0124
>> +#define WIN3_DSP_ST1 0x0128
>> +#define WIN3_SRC_ALPHA_CTRL 0x012c
>> +#define WIN3_MST2 0x0130
>> +#define WIN3_DSP_INFO2 0x0134
>> +#define WIN3_DSP_ST2 0x0138
>> +#define WIN3_DST_ALPHA_CTRL 0x013c
>> +#define WIN3_MST3 0x0140
>> +#define WIN3_DSP_INFO3 0x0144
>> +#define WIN3_DSP_ST3 0x0148
>> +#define WIN3_FADING_CTRL 0x014c
>> +/* hwc register */
>> +#define HWC_CTRL0 0x0150
>> +#define HWC_CTRL1 0x0154
>> +#define HWC_MST 0x0158
>> +#define HWC_DSP_ST 0x015c
>> +#define HWC_SRC_ALPHA_CTRL 0x0160
>> +#define HWC_DST_ALPHA_CTRL 0x0164
>> +#define HWC_FADING_CTRL 0x0168
>> +/* post process register */
>> +#define POST_DSP_HACT_INFO 0x0170
>> +#define POST_DSP_VACT_INFO 0x0174
>> +#define POST_SCL_FACTOR_YRGB 0x0178
>> +#define POST_SCL_CTRL 0x0180
>> +#define POST_DSP_VACT_INFO_F1 0x0184
>> +#define DSP_HTOTAL_HS_END 0x0188
>> +#define DSP_HACT_ST_END 0x018c
>> +#define DSP_VTOTAL_VS_END 0x0190
>> +#define DSP_VACT_ST_END 0x0194
>> +#define DSP_VS_ST_END_F1 0x0198
>> +#define DSP_VACT_ST_END_F1 0x019c
>> +/* register definition end */
>> +
>> +/* interrupt define */
>> +#define DSP_HOLD_VALID_INTR (1 << 0)
>> +#define FS_INTR (1 << 1)
>> +#define LINE_FLAG_INTR (1 << 2)
>> +#define BUS_ERROR_INTR (1 << 3)
>> +
>> +#define DSP_HOLD_VALID_INTR_EN(x) ((x) << 4)
>> +#define FS_INTR_EN(x) ((x) << 5)
>> +#define LINE_FLAG_INTR_EN(x) ((x) << 6)
>> +#define BUS_ERROR_INTR_EN(x) ((x) << 7)
>> +#define DSP_HOLD_VALID_INTR_MASK (1 << 4)
>> +#define FS_INTR_EN_MASK (1 << 5)
>> +#define LINE_FLAG_INTR_MASK (1 << 6)
>> +#define BUS_ERROR_INTR_MASK (1 << 7)
>> +
>> +#define DSP_HOLD_VALID_INTR_CLR (1 << 8)
>> +#define FS_INTR_EN_CLR (1 << 9)
>> +#define LINE_FLAG_INTR_CLR (1 << 10)
>> +#define BUS_ERROR_INTR_CLR (1 << 11)
>> +#define DSP_LINE_NUM(x) (((x) & 0x1fff) << 12)
>> +#define DSP_LINE_NUM_MASK (0x1fff << 12)
>> +
>> +/* src alpha ctrl define */
>> +#define SRC_FADING_VALUE(x) (((x) & 0xff) << 24)
>> +#define SRC_GLOBAL_ALPHA(x) (((x) & 0xff) << 16)
>> +#define SRC_FACTOR_M0(x) (((x) & 0x7) << 6)
>> +#define SRC_ALPHA_CAL_M0(x) (((x) & 0x1) << 5)
>> +#define SRC_BLEND_M0(x) (((x) & 0x3) << 3)
>> +#define SRC_ALPHA_M0(x) (((x) & 0x1) << 2)
>> +#define SRC_COLOR_M0(x) (((x) & 0x1) << 1)
>> +#define SRC_ALPHA_EN(x) (((x) & 0x1) << 0)
>> +/* dst alpha ctrl define */
>> +#define DST_FACTOR_M0(x) (((x) & 0x7) << 6)
>> +
>> +/*
>> + * display output interface supported by rockchip lcdc
>> + */
>> +#define ROCKCHIP_OUT_MODE_P888 0
>> +#define ROCKCHIP_OUT_MODE_P666 1
>> +#define ROCKCHIP_OUT_MODE_P565 2
>> +/* for use special outface */
>> +#define ROCKCHIP_OUT_MODE_AAAA 15
>> +
>> +enum alpha_mode {
>> + ALPHA_STRAIGHT,
>> + ALPHA_INVERSE,
>> +};
>> +
>> +enum global_blend_mode {
>> + ALPHA_GLOBAL,
>> + ALPHA_PER_PIX,
>> + ALPHA_PER_PIX_GLOBAL,
>> +};
>> +
>> +enum alpha_cal_mode {
>> + ALPHA_SATURATION,
>> + ALPHA_NO_SATURATION,
>> +};
>> +
>> +enum color_mode {
>> + ALPHA_SRC_PRE_MUL,
>> + ALPHA_SRC_NO_PRE_MUL,
>> +};
>> +
>> +enum factor_mode {
>> + ALPHA_ZERO,
>> + ALPHA_ONE,
>> + ALPHA_SRC,
>> + ALPHA_SRC_INVERSE,
>> + ALPHA_SRC_GLOBAL,
>> +};
>> +
>> +#endif /* _ROCKCHIP_DRM_VOP_H */
>> --
>> 1.7.9.5
>>
>>
>
>
[-- Attachment #1.2: Type: text/html, Size: 130663 bytes --]
[-- Attachment #2: Type: text/plain, Size: 159 bytes --]
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Eric W. Biederman @ 2014-09-29 18:43 UTC (permalink / raw)
To: nicolas.dichtel-pdR9zngts4EAvxtiuMwx3w
Cc: Network Development, Linux Containers,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Andy Lutomirski, Stephen Hemminger, Cong Wang, Linux API,
Andrew Morton, David S. Miller
In-Reply-To: <54294B4E.70501-pdR9zngts4EAvxtiuMwx3w@public.gmane.org>
Nicolas Dichtel <nicolas.dichtel@6wind.com> writes:
> Le 26/09/2014 20:57, Eric W. Biederman a écrit :
>> Andy Lutomirski <luto@amacapital.net> writes:
>>
>>> On Fri, Sep 26, 2014 at 11:10 AM, Eric W. Biederman
>>> <ebiederm@xmission.com> wrote:
>>>> Nicolas Dichtel <nicolas.dichtel@6wind.com> writes:
>>>>
>>>>> The goal of this serie is to be able to multicast netlink messages with an
>>>>> attribute that identify a peer netns.
>>>>> This is needed by the userland to interpret some informations contained in
>>>>> netlink messages (like IFLA_LINK value, but also some other attributes in case
>>>>> of x-netns netdevice (see also
>>>>> http://thread.gmane.org/gmane.linux.network/315933/focus=316064 and
>>>>> http://thread.gmane.org/gmane.linux.kernel.containers/28301/focus=4239)).
>>>>
>>>> I want say that the problem addressed by patch 3/5 of this series is a
>>>> fundamentally valid problem. We have network objects spanning network
>>>> namespaces and it would be very nice to be able to talk about them in
>>>> netlink, and file descriptors are too local and argubably too heavy
>>>> weight for netlink quires and especially for netlink broadcast messages.
>>>>
>>>> Furthermore the concept of ineternal concept of peernet2id seems valid.
>>>>
>>>> However what you do not address is a way for CRIU (aka process
>>>> migration) to be able to restore these ids after process migration.
>>>> Going farther it looks like you are actively breaking process migration
>>>> at this time, making this set of patches a no-go.
> Ok, I will look more deeply into CRIU.
>
>>>>
>>>> When adding a new form of namespace id CRIU patches are just about
>>>> as necessary as iproute patches.
> Noted.
>>>> That does not describe what you have actually implemented in the
>>>> patches.
>>>>
>>>> I see two ways to go with this.
>>>>
>>>> - A per network namespace table to that you can store ids for ``peer''
>>>> network namespaces. The table would need to be populated manually by
>>>> the likes of ip netns add.
>>>>
>>>> That flips the order of assignment and makes this idea solid.
> I have a preference for this solution, because it allows to have a full
> broadcast messages. When you have a lot of network interfaces (> 10k),
> it saves a lot of time to avoid another request to get all informations.
My practical question is how often does it happen that we care?
>>>> Unfortunately in the case of a fully referencing mesh of N network
>>>> namespaces such a mesh winds up taking O(N^2) space, which seems
>>>> undesirable.
> Memory consumption vs performances ;-)
> In fact, when you have a lot of netns, you already should have some memory
> available (at least N lo interfaces + N interfaces (veth or a x-netns
> interface)). I'm not convinced that this is really an obstacle.
I would have to see how it all fits together. O(N^2) grows a lot faster
that N. So after a point it isn't in the same ballpark of memory
consumption.
>> broadcast message business, and only care about the remote namespace for
>> unicast messages. Putting the work in an infrequently used slow path
>> instead of a comparitively common path gives us much more freedom in
>> the implementation.
> I think it's better to have a full netlink messages, instead a partial one.
> There is already a lot of attributes added for each rtnl interface messages to
> be sure to describe all parameters of these interfaces.
> And if the user don't care about ids (user has not set any id with iproute2),
> we can just add the same attribute with id 0 (let's say it's a reserved id) to
> indicate that the link part of this interface is in another netns.
I imagine an id like that is something we would want ip netns add to
set, and probably set in all existing network namespaces as well.
> The great benefit of your first proposal is that the ids are set by the
> userspace and thus it allows a high flexibility.
>
> Would you accept a patch that implements this first solution?
I would not fundamentally reject it. I would really like to make
certain we think through how it will be used and what the practical
benefits are. Depending on how it is used the data structure could
be a killer or it could be a case where we see how to manage it and
simply don't care.
Eric
_______________________________________________
Containers mailing list
Containers@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/containers
^ permalink raw reply
* Re: [PATCH] usb: gadget: f_rndis: fix usb_interface_descriptor for rndis
From: Lars Melin @ 2014-09-29 16:05 UTC (permalink / raw)
To: hs
Cc: linux-usb, Felipe Balbi, Greg Kroah-Hartman, linux-kernel, netdev,
linux-api, Andrzej Pietrasiewicz, Michal Nazarewicz,
Kyungmin Park, Dan Carpenter, Macpaul Lin, Meier, Roger
In-Reply-To: <54294C78.6050006@denx.de>
On 2014-09-29 19:11, Heiko Schocher wrote:
> Hello Lars,
>
> sorry for my late answer ...
>
> Am 24.09.2014 16:22, schrieb Lars Melin:
>> On 2014-09-24 20:12, Heiko Schocher wrote:
>>> Hello Lars,
>>>
>>> Am 24.09.2014 14:25, schrieb Lars Melin:
>>>> On 2014-09-24 13:48, Heiko Schocher wrote:
>>>>> use the values for RNDIS over Ethernet as defined in
>>>>> http://www.usb.org/developers/defined_class
>>>>> (search for RDNIS):
>>>>>
>>>>> - baseclass: 0xef (miscellaneous)
>>>>> - subclass: 0x04
>>>>> - protocol: 0x01
>>>>>
>>>> That is usb class, it is not the same thing as communication device
>>>> class.
>>>>> --- a/include/uapi/linux/usb/cdc.h
>>>>> +++ b/include/uapi/linux/usb/cdc.h
>>>>> @@ -12,6 +12,7 @@
>>>>> #include <linux/types.h>
>>>>> #define USB_CDC_SUBCLASS_ACM 0x02
>>>>> +#define USB_CDC_SUBCLASS_RNDIS 0x04
>>>> No, no, no.
>>>> There is no CDC_SUBCLASS_RNDIS and you can not define one over an
>>>> already used cdc subclass number, 0x04 is Multi-Channel Control Model
>>>
>>> Ah, ok, so I have to define this values in a new header file, as there
>>> is no current file for the USB_CLASS_MISC defines? Or is there a proper
>>> place for them?
>>>
>>> BTW: where do I find the "cdc subclass number, 0x04 is Multi-Channel
>>> Control Model" define?
>>>
>>> bye,
>>> Heiko
>>
>> You can still find the original specification usbcdc11.pdf on the net
>> if you google for it, it has been pulled from usb.org where you could
>> download it until a few years ago.
>> It is old but covers a lot of what you need to know.
>
> Hmm.. maybe I am to dummy for finding this docment...
>
> http://www.usb.org/results?q=usbcdc11.pdf&submit=Search
>
> does not find this document ... could you send me a direct link?
>
> I found with the above search:
>
> http://www.usb.org/developers/defined_class
I don't know if it is a good idea to provide a link here to a document
which usb.org has made unavailable, I told you to google for the file
name , not to search for it on usb.org
> and this site, exactly describes the values for RNDIS over ethernet,
> as my patch changes [1]
>
>> Linux has afaik only the cdc.h definition file, everything else is
>> coded by class/subclass in respectively drivers when needed.
>
> why not in header files? I thought, magical values are not welcome
> in source code ...
>
I was wrong, usb class definitions are included in
../include/uapi/linux/usb/ch9.h
> As for the is_rndis() function case, this function is defined in
> 2 places:
>
> - drivers/net/usb/cdc_ether.c
> - drivers/usb/core/generic.c
>
> Has this a special reason? This seems suboptimal to me ...
Yes it has, but the core driver is not an interface driver so it is not
of relevance in this case.
cdc_ether handles interfaces of device connected to the usb bus, not
interfaces of gadget devices
created by linux.
> I got from a customer this patch (in a similiar version) and
> he did tests with [3] and saw, that a board which runs linux,
> is seen in [3] with the values [2] ... so he changed the
> values in drivers/usb/gadget/function/f_rndis.c to the
> values [1], which are documented in [4] and with them
> the test [3] is happy ... and the file
> "Documentation/usb/linux.inf" is not longer needed on the
> windows pc!
>
The patch from your customer removed the most common rndis interface
attributes and substituted them
with one of many other interface attributes which Microsoft uses, this
is not the right way of doing it.
Why did he patch ../core/generic.c and ../net/usb/cdc_ether.c if he
wants to change the interface attributes of g_rndis?
Lars
^ permalink raw reply
* Re: [PATCH 4/4] Adds ioctl interface support for ext4 project
From: Jan Kara @ 2014-09-29 15:55 UTC (permalink / raw)
To: Dave Chinner
Cc: Jan Kara, Christoph Hellwig, adilger-m1MBpc4rdrD3fQ9qLvQP4Q,
linux-api-u79uwXL29TY76Z2rM5mHXA, xfs-VZNHf3L845pBDgjK7y7TUQ,
dmonakhov-GEFAQzZX7r8dnm+yROfE0A,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn, Li Xi,
linux-fsdevel-u79uwXL29TY76Z2rM5mHXA, tytso-3s7WtUTddSA,
linux-ext4-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20140925224225.GJ4945@dastard>
On Fri 26-09-14 08:42:25, Dave Chinner wrote:
> On Thu, Sep 25, 2014 at 03:52:13PM +0200, Jan Kara wrote:
> > On Thu 25-09-14 17:59:12, Dave Chinner wrote:
> > > On Wed, Sep 24, 2014 at 07:01:05PM +0200, Jan Kara wrote:
> > > > On Wed 24-09-14 09:26:34, Christoph Hellwig wrote:
> > > > > On Wed, Sep 24, 2014 at 06:25:07PM +0200, Jan Kara wrote:
> > > > > > On Wed 24-09-14 22:04:30, Li Xi wrote:
> > > > > > > This patch adds ioctl interface for setting/getting project of ext4.
> > > > > > The patch looks good to me. I was just wondering whether it won't be
> > > > > > useful to add an ioctl() which isn't ext4 specific. We could just extend
> > > > > > ->setattr() to allow setting of project ID (most filesystems would just
> > > > > > return -EOPNOTSUPP but ext4 and xfs could do the right thing) and then call
> > > > > > ->setattr from the generic ioctl. That way userspace won't have to care
> > > > > > about filesystem type when setting project ID... What do others think?
> > > > >
> > > > > Absolutely. In general I also wonder why this patch doesn't implement
> > > > > the full XFS API. Maybe there is a reason it was considered and
> > > > > rejected, but it would be helpful to document why.
> > > > Do you mean full get/setfsxattr API?
> > >
> > > That's a good start.
> > >
> > > The bigger issue in my mind is that we already have a fully featured
> > > quota API that supports project quotas and userspace tools available
> > > that manipulate it. xfstests already uses those tools and API
> > > for testing project quotas.
> > Well, the VFS quota API is trivially extended by adding additional quota
> > type so I don't really see about which reinventing of quota API are you
> > speaking here...
>
> It doesn't seem quite as trivial as you make out given all thei
> issues so far around increasing MAXQUOTA, the increase in size of
> the inode, etc.
Well, troubles with increasing MAXQUOTAS is more about expectations
that VFS quota subsystem can support only 2 quota types. So we have to do
those changes regardless of interface we choose.
There is one change necessary in the interface (not done yet) and that is
that filesystems using VFS quotas and not supporting project quotas will
need to refuse quotactls for project quotas. This won't be necessary if we
simply refuse to manipulate project quotas using the standard VFS
interface. But I wouldn't call it difficult either.
> > > This whole patchset reinvents all the quota APIs, and will require
> > > adding support in userspace, and hence require re-inventing all the
> > > test infrastructure we already have because it won't be compatible
> > > with the existing project quota test code.
> > Well, quota-tools will have to extended to know about the new quota type.
> > Yes. But that's easy to do. I think teaching xfs quota tools to work with
> > ext4 will be a bigger project plus I don't think I want to force sysadmins
> > which are used to work with quota-tools to switch to other utilities just
> > because of project quotas.
> >
> > Regarding xfstests - I've checked and most of the project quota tests in
> > xfs directory aren't directly usable for ext4 anyway because of other
> > functionality ext4 doesn't support. So we'll need to distill the least
> > common denominator from them anyway...
>
> I just did a quick scan - of the ~13 tests in tests/xfs that
> exercise project quotas, only 2 of them test things that are xfs
> specific (e.g. use xfs_db to peer at things, or use xfs_admin, etc).
> The rest all rely on xfs_quota to manage and configure project
> quotas but otherwise don't do anything XFS specific.
Yeah, I messed up my original check (I originally found like 5 project
quota related tests and they were those problematic). I checked again and
most of them should be relatively easy to adapt (we'll need some changes
for mount options handling but that's inevitable).
> We want project quotas to have the same management interface for
> administrators regardless of the filesystem they are using. The only
> way we can do that is to ensure that the same tools work on either
> filesystem, and right now it seems to me that the ext4 NIH syndrome
> is winning out over what is best for our users...
>
> Look, I have no problems with extending the existing quota
> interfaces to support project quotas, but that should be a
> *secondary* improvement as userspace tools are updated. The
> primary goal needs to be "works identically to XFS" and so it needs
> to implement the interfaces that are currently used for management
> so that we can actually test that it does work identically.
So I had another look at the quotactl interface we are speaking about.
XFS has the following commands there:
Q_XQUOTAON
Q_XQUOTAOFF
These could be relatively easily hooked up to call appropriate VFS
functions.
Q_XQUOTARM
This doesn't have equivalent in VFS and currently I'm not convinced we
want to do the work in filesystems to support this...
Q_XGETQSTAT
Q_XGETQSTATV
This corresponds to Q_GETINFO of VFS quotas although it provides more
information. We don't easily have things like number of incore dquots or
number of file extents available. There's also no limit on the number of
warnings. But other than that diverting these to VFS interfaces through a
translation function should be easy. Any idea on what numbers should we
present from VFS?
BTW how do you set the information? We have Q_SETINFO for that in VFS
quotas.
Q_XSETQLIM
Q_XGETQUOTA
These are already handled so they work regardless of the underlying fs
type.
Q_XQUOTASYNC
This is the same as Q_SYNC. For VFS quotas we need to do some work in some
cases.
So all in all it seems relatively easy to make the VFS and XFS quota
interfaces more compatible than they are now and it's a direction I like.
I can have a look into that once I finish patches to move i_dquot[] array
out of generic inode (which is desirable regardless of project quotas).
Honza
--
Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
SUSE Labs, CR
^ permalink raw reply
* Re: [PATCH 2/2] perf: Userspace software event and ioctl
From: Pawel Moll @ 2014-09-29 15:53 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Richard Cochran, Steven Rostedt, Ingo Molnar, Paul Mackerras,
Arnaldo Carvalho de Melo, John Stultz,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20140929153257.GM4140-IIpfhp3q70z/8w/KjCw3T+5/BudmfyzbbVWyRVo5IupeoWH0uzbU5w@public.gmane.org>
On Mon, 2014-09-29 at 16:32 +0100, Peter Zijlstra wrote:
> Also none of the many words above describe
> PERF_SAMPLE_USERSPACE_EVENT(), wth is that about?
Hopefully description of the v2 makes better job in this:
http://thread.gmane.org/gmane.linux.kernel/1793272/focus=4813
where it's already called "UEVENT" and was generated by write().
Before you get into this, though, the most important outcomes of both v1
and v2 discussions:
* Ingo suggested prctl(PR_TRACE_UEVENT, type, size, data, 0) as the way
of generating such events (so the tracee doesn't have to know the fd to
do ioctl); Frederic seems to have the same on his mind.
* Namhyung proposed sticking the userspace-originating events into the
buffer as PERF_RECORD_UEVENT rather then PERF_SAMPLE_UEVENT.
Working on making both happen now.
Pawel
^ permalink raw reply
* Re: [PATCH 1/2] perf: Add sampling of the raw monotonic clock
From: Pawel Moll @ 2014-09-29 15:45 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Richard Cochran, Steven Rostedt, Ingo Molnar, Paul Mackerras,
Arnaldo Carvalho de Melo, John Stultz,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Thomas Gleixner
In-Reply-To: <20140929152832.GL4140-IIpfhp3q70z/8w/KjCw3T+5/BudmfyzbbVWyRVo5IupeoWH0uzbU5w@public.gmane.org>
On Mon, 2014-09-29 at 16:28 +0100, Peter Zijlstra wrote:
> On Thu, Sep 18, 2014 at 03:34:32PM +0100, Pawel Moll wrote:
> > @@ -4456,6 +4459,13 @@ static void __perf_event_header__init_id(struct perf_event_header *header,
> > data->cpu_entry.cpu = raw_smp_processor_id();
> > data->cpu_entry.reserved = 0;
> > }
> > +
> > + if (sample_type & PERF_SAMPLE_CLOCK_RAW_MONOTONIC) {
> > + struct timespec now;
> > +
> > + getrawmonotonic(&now);
> > + data->clock_raw_monotonic = timespec_to_ns(&now);
> > + }
> > }
> >
>
> This cannot work, getrawmonotonic() isn't NMI-safe and there's
> nothing stopping this being used from NMI context.
>
> Also getrawmonotonic() + timespec_to_ns() will make tglx sad, he's just
> done a tree-wide eradication of silly conversions and now you're adding
> a ns -> timespec -> ns dance right back.
Last thing I want is to make Thomas sad... For obvious reasons ;-)
> I _think_ you want ktime_get_mono_fast_ns(),
With pleasure, it's exactly what I need.
> but this does bring us
> right back to the question/discussion on which timebase you'd want to
> sync again. MONO does make sense for most cases, but I think we've had
> fairly sane stories for people wanting to sync against other clocks.
Yes. I've asked the same question somewhere in the thread.
ftrace has got a switch and a selection of trace_clocks in
kernel/trace/trace.c - do we want something similar (in integer form
probably, though) in perf_events.h with an additional "flag" in struct
perf_event_attr? It could be used to pick a time source for
PERF_SAMPLE_CLOCK (PERF_SAMPLE_TRACE_CLOCK?) sample.
Pawel
^ permalink raw reply
* Re: [PATCH 2/2] perf: Userspace software event and ioctl
From: Peter Zijlstra @ 2014-09-29 15:32 UTC (permalink / raw)
To: Pawel Moll
Cc: Richard Cochran, Steven Rostedt, Ingo Molnar, Paul Mackerras,
Arnaldo Carvalho de Melo, John Stultz, linux-kernel, linux-api
In-Reply-To: <1411050873-9310-3-git-send-email-pawel.moll@arm.com>
On Thu, Sep 18, 2014 at 03:34:33PM +0100, Pawel Moll wrote:
> This patch adds a PERF_COUNT_SW_USERSPACE_EVENT type,
> which can be generated by user with PERF_EVENT_IOC_ENTRY
> ioctl command, which injects an event of said type into
> the perf buffer.
>
> The ioctl takes a pointer to struct perf_event_userspace
> as an argument. The structure begins with a 64-bit
> integer type value, which determines meaning of the
> following content (size/data pair). Type 0 are defined
> as zero-terminated strings, other types are defined by
> userspace (the perf tool will contain a list of
> known values with reference implementation of data
> content parsers).
>
> Possible use cases for this feature:
>
> - "perf_printf" like mechanism to add logging messages
> to one's perf session; an example implementation:
>
> int perf_printf(int perf_fd, const char *fmt, ...)
> {
> struct perf_event_userspace *event;
> int size;
> va_list ap;
> int err;
>
> va_start(ap, fmt);
>
> size = vsnprintf(NULL, 0, fmt, ap) + 1;
> event = malloc(sizeof(*event) + size);
> if (!event) {
> va_end(ap);
> return -1;
> }
>
> event->type = 0;
> event->size = size;
> vsnprintf(event->data, size, fmt, ap);
>
> va_end(ap);
>
> err = ioctl(perf_fd, PERF_EVENT_IOC_USERSPACE, event);
>
> free(event);
>
> return err < 0 ? err : size - 1;
> }
>
> - "perf_printf" used by for perf trace tool,
> where certain traced process' calls are intercepted
> (eg. using LD_PRELOAD) and treated as logging
> requests, with it output redirected into the
> perf buffer
>
> - synchronisation of performance data generated in
> user space with the perf stream coming from the kernel.
> For example, the marker can be inserted by a JIT engine
> after it generated portion of the code, but before the
> code is executed for the first time, allowing the
> post-processor to pick the correct debugging
> information.
>
> - other example is a system profiling tool taking data
> from other sources than just perf, which generates a marker
> at the beginning at at the end of the session
> (also possibly periodically during the session) to
> synchronise kernel timestamps with clock values
> obtained in userspace (gtod or raw_monotonic).
Feel free to use up to 70 chars wide text in Changelogs. Most editors
have support for reflowing text. No need to keep it this narrow.
Also none of the many words above describe
PERF_SAMPLE_USERSPACE_EVENT(), wth is that about?
^ permalink raw reply
* Re: [PATCH 1/2] perf: Add sampling of the raw monotonic clock
From: Peter Zijlstra @ 2014-09-29 15:28 UTC (permalink / raw)
To: Pawel Moll
Cc: Richard Cochran, Steven Rostedt, Ingo Molnar, Paul Mackerras,
Arnaldo Carvalho de Melo, John Stultz,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-api-u79uwXL29TY76Z2rM5mHXA, Thomas Gleixner
In-Reply-To: <1411050873-9310-2-git-send-email-pawel.moll-5wv7dgnIgG8@public.gmane.org>
On Thu, Sep 18, 2014 at 03:34:32PM +0100, Pawel Moll wrote:
> @@ -4456,6 +4459,13 @@ static void __perf_event_header__init_id(struct perf_event_header *header,
> data->cpu_entry.cpu = raw_smp_processor_id();
> data->cpu_entry.reserved = 0;
> }
> +
> + if (sample_type & PERF_SAMPLE_CLOCK_RAW_MONOTONIC) {
> + struct timespec now;
> +
> + getrawmonotonic(&now);
> + data->clock_raw_monotonic = timespec_to_ns(&now);
> + }
> }
>
This cannot work, getrawmonotonic() isn't NMI-safe and there's
nothing stopping this being used from NMI context.
Also getrawmonotonic() + timespec_to_ns() will make tglx sad, he's just
done a tree-wide eradication of silly conversions and now you're adding
a ns -> timespec -> ns dance right back.
I _think_ you want ktime_get_mono_fast_ns(), but this does bring us
right back to the question/discussion on which timebase you'd want to
sync again. MONO does make sense for most cases, but I think we've had
fairly sane stories for people wanting to sync against other clocks.
A well..
^ permalink raw reply
* Re: [PATCH 2/2] perf: Userspace software event and ioctl
From: Pawel Moll @ 2014-09-29 14:52 UTC (permalink / raw)
To: Frederic Weisbecker
Cc: Ingo Molnar, Ingo Molnar, Arnaldo Carvalho de Melo,
Richard Cochran, Steven Rostedt, Peter Zijlstra, Paul Mackerras,
John Stultz, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAFTL4hy1d8twv2tGxc4EhCeDm7ApnH7SuK26W1yaekKhCrPMZA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Sat, 2014-09-27 at 18:14 +0100, Frederic Weisbecker wrote:
> 2014-09-25 20:33 GMT+02:00 Ingo Molnar <mingo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>:
> >
> > * Pawel Moll <pawel.moll-5wv7dgnIgG8@public.gmane.org> wrote:
> >
> >> On Wed, 2014-09-24 at 08:49 +0100, Ingo Molnar wrote:
> >> > * Pawel Moll <pawel.moll-5wv7dgnIgG8@public.gmane.org> wrote:
> >> >
> >> > > On Thu, 2014-09-18 at 15:34 +0100, Pawel Moll wrote:
> >> > > > This patch adds a PERF_COUNT_SW_USERSPACE_EVENT type,
> >> > > > which can be generated by user with PERF_EVENT_IOC_ENTRY
> >> > > > ioctl command, which injects an event of said type into
> >> > > > the perf buffer.
> >> > >
> >> > > It occurred to me last night that currently perf doesn't handle "write"
> >> > > syscall at all, while this seems like the most natural way of
> >> > > "injecting" userspace events into perf buffer.
> >> > >
> >> > > An ioctl would still be needed to set a type of the following events,
> >> > > something like:
> >> > >
> >> > > ioctl(SET_TYPE, 0x42);
> >> > > write(perf_fd, binaryblob, size);
> >> > > ioctl(SET_TYPE, 0);
> >> > > dprintf(perf_fd, "String");
> >> > >
> >> > > which is fine for use cases when the type doesn't change often,
> >> > > but would double the amount of syscalls when every single event
> >> > > is of a different type. Perhaps there still should be a
> >> > > "generating ioctl" taking both type and data/size in one go?
> >> >
> >> > Absolutely, there should be a single syscall.
> >>
> >> Yeah, it's my gut feeling as well. I just wonder if we still want to
> >> keep write() handler for operations on perf fds? This seems natural -
> >> takes data buffer and its size. The only issue is the type.
> >>
> >> > I'd even argue it should be a new prctl(): that way we could both
> >> > generate user events for specific perf fds, but also into any
> >> > currently active context (that allows just generation/injection
> >> > of user events). In the latter case we might have no fd to work
> >> > off from.
> >>
> >> When Arnaldo suggested that the "user events" could be used by perf
> >> trace, it was exactly my first thought. I just didn't have answer how to
> >> present it to the user (an extra syscall didn't seem like a good idea),
> >> but prctl seems interesting, something like this?
> >>
> >> prctl(PR_TRACE_UEVENT, type, size, data, 0);
> >
> > Exactly!
> >
> >> How would we select tasks that can write to a given buffer? Maybe an
> >> ioctl() on a perf fd? Something like this?
> >>
> >> ioctl(perf_fd, PERF_EVENT_IOC_ENABLE_UEVENT, pid);
> >> ioctl(perf_fd, PERF_EVENT_IOC_DISABLE_UEVENT, pid);
> >
> > No, I think there's a simpler way: this should be a regular
> > perf_attr flag, which defaults to '0' (tasks cannot do this), but
> > which can be set to 1 if the profiler explicitly allows such
> > event injection.
>
> Maybe we just don't even need any permission at all. Which harm can
> that do if this only ever generate events to those interested in the
> relevant perf context? It could be a simple tracepoint BTW.
Yeah, Ingo already pointed it out (that non-root task can't trace root
tasks anyway).
> Oh and I really like the fact we don't use a syscall that requires an
> fd. The tracee really shouldn't be aware of the tracer.
Agreed, I'll look at solution with prctl() this week.
Pawel
^ permalink raw reply
* Re: [PATCH v2 1/2] perf: Add sampling of the raw monotonic clock
From: Pawel Moll @ 2014-09-29 14:47 UTC (permalink / raw)
To: David Ahern
Cc: Namhyung Kim, Richard Cochran, Steven Rostedt, Ingo Molnar,
Peter Zijlstra, Paul Mackerras, Arnaldo Carvalho de Melo,
John Stultz, linux-kernel@vger.kernel.org,
linux-api@vger.kernel.org
In-Reply-To: <5425BD94.4030006@gmail.com>
On Fri, 2014-09-26 at 20:25 +0100, David Ahern wrote:
> On 9/26/14, 9:05 AM, Pawel Moll wrote:
> > To do the correlation you need both timestamps to be "taken"
> > simultaneously:
> >
> > perf event user event
> > -----O--------------+-------------O------> t_mono
> > : | :
> > : V :
> > -----O----------------------------O------> t_perf
> >
> > Of course it's not possible get both values literally at the same time,
> > but placing them in a atomic context a couple of instructions from each
> > other still gives pretty good results. The larger this distance is, the
>
> An early patchset on this topic added the realtime clock as an event and
> an ioctl was used to push a sample into the event stream.
Yeah, I remember. If I remember correctly correctly the pushback was on
a custom event type, right?
Generally speaking I don't mind any solution that we'll get us to the
place both you and I want to be (just being able to time stamp some
performance data in userspace, how difficult can this be! ;-) but I like
the flexibility of an extra sample - one can pick and mix events and
samples at one's leisure.
> In that case
> you have wall clock and perf-clock samples taken in the same kernel
> context and about as close together as you can get.
Yep, that's what I was saying - we can't quite get two timestamps at the
*same*, but getting them within a single atomic block of instructions
gives reasonable accuracy.
Thanks!
Pawel
^ permalink raw reply
* Re: [PATCH] usb: gadget: f_rndis: fix usb_interface_descriptor for rndis
From: Heiko Schocher @ 2014-09-29 12:11 UTC (permalink / raw)
To: Lars Melin
Cc: linux-usb, Felipe Balbi, Greg Kroah-Hartman, linux-kernel, netdev,
linux-api, Andrzej Pietrasiewicz, Michal Nazarewicz,
Kyungmin Park, Dan Carpenter, Macpaul Lin, Meier, Roger
In-Reply-To: <5422D39D.70006@gmail.com>
Hello Lars,
sorry for my late answer ...
Am 24.09.2014 16:22, schrieb Lars Melin:
> On 2014-09-24 20:12, Heiko Schocher wrote:
>> Hello Lars,
>>
>> Am 24.09.2014 14:25, schrieb Lars Melin:
>>> On 2014-09-24 13:48, Heiko Schocher wrote:
>>>> use the values for RNDIS over Ethernet as defined in
>>>> http://www.usb.org/developers/defined_class
>>>> (search for RDNIS):
>>>>
>>>> - baseclass: 0xef (miscellaneous)
>>>> - subclass: 0x04
>>>> - protocol: 0x01
>>>>
>>> That is usb class, it is not the same thing as communication device class.
>>>> --- a/include/uapi/linux/usb/cdc.h
>>>> +++ b/include/uapi/linux/usb/cdc.h
>>>> @@ -12,6 +12,7 @@
>>>> #include <linux/types.h>
>>>> #define USB_CDC_SUBCLASS_ACM 0x02
>>>> +#define USB_CDC_SUBCLASS_RNDIS 0x04
>>> No, no, no.
>>> There is no CDC_SUBCLASS_RNDIS and you can not define one over an already used cdc subclass number, 0x04 is Multi-Channel Control Model
>>
>> Ah, ok, so I have to define this values in a new header file, as there
>> is no current file for the USB_CLASS_MISC defines? Or is there a proper
>> place for them?
>>
>> BTW: where do I find the "cdc subclass number, 0x04 is Multi-Channel
>> Control Model" define?
>>
>> bye,
>> Heiko
>
> You can still find the original specification usbcdc11.pdf on the net if you google for it, it has been pulled from usb.org where you could download it until a few years ago.
> It is old but covers a lot of what you need to know.
Hmm.. maybe I am to dummy for finding this docment...
http://www.usb.org/results?q=usbcdc11.pdf&submit=Search
does not find this document ... could you send me a direct link?
I found with the above search:
http://www.usb.org/developers/defined_class
and this site, exactly describes the values for RNDIS over ethernet,
as my patch changes [1]
> Linux has afaik only the cdc.h definition file, everything else is coded by class/subclass in respectively drivers when needed.
why not in header files? I thought, magical values are not welcome
in source code ...
As for the is_rndis() function case, this function is defined in
2 places:
- drivers/net/usb/cdc_ether.c
- drivers/usb/core/generic.c
Has this a special reason? This seems suboptimal to me ...
> 02/02/ff or e0/01/03 are the most common interface attribute for rndis, both of them together with a data interface with attributes 0a/00/00.
I must admit, I am not a USB nor a RNDIS expert ...
> Please check the whitelisting in drivers/net/usb/rndis_host.c and also blacklistings in other net drivers under the same path, it should give you an idea how to bind an interface to a specific driver by interface attributes and/or usb vid:pid.
> You should be able to do the same for your particular device.
Hmm.. I did not understand you here ... so, one step back:
I got from a customer this patch (in a similiar version) and
he did tests with [3] and saw, that a board which runs linux,
is seen in [3] with the values [2] ... so he changed the
values in drivers/usb/gadget/function/f_rndis.c to the
values [1], which are documented in [4] and with them
the test [3] is happy ... and the file
"Documentation/usb/linux.inf" is not longer needed on the
windows pc!
So he (and at the end I too) thought, that this is the proper
way to make [3] happy ... (maybe [3] is incorrect ? )
Is current ML code correct? And if yes, why?
If the values [2] in current ML linux are correct,
could you say me, where they are documented?
(and sorry for my stupid questions ...)
Thanks!
bye,
Heiko
[1] values which my patch sets for RNDIS over ethernet
- baseclass: 0xef (miscellaneous)
- subclass: 0x04
- protocol: 0x01
[2] currently used values for RNDIS over ethernet
- baseclass: 0x02 (USB_CLASS_COMM)
- subclass: 0x02
- protocol: 0xff
[3] "USB Compliance test suite which runs Windows", see:
http://www.usb.org/developers/tools/usb20_tools/#usb20cv
[4] http://www.usb.org/developers/defined_class
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox