On 2014年09月24日 16:20, Daniel Vetter wrote:
On Mon, Sep 22, 2014 at 06:48:54PM +0800, Mark yao wrote:
This patch adds the basic structure of a DRM Driver for Rockchip Socs.
Signed-off-by: Mark yao mark.yao@rock-chips.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
drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/rockchip/Kconfig | 19 + drivers/gpu/drm/rockchip/Makefile | 10 + drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 524 ++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 120 +++ drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 201 ++++ drivers/gpu/drm/rockchip/rockchip_drm_fb.h | 28 + drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 231 +++++ drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h | 20 + drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 404 ++++++++ drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 72 ++ drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1372 +++++++++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 187 ++++ include/uapi/drm/rockchip_drm.h | 75 ++
uapi is still here ... Was this an oversight? -Daniel
Hi, Daniel this version is old, newest is v5. and I remove uapi at v5. you can see v5 patch at: https://lkml.org/lkml/2014/9/23/1061 thanks -Mark
15 files changed, 3266 insertions(+) create mode 100644 drivers/gpu/drm/rockchip/Kconfig create mode 100644 drivers/gpu/drm/rockchip/Makefile create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.c create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.h create mode 100644 include/uapi/drm/rockchip_drm.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index b066bb3..7c4c3c6 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -171,6 +171,8 @@ config DRM_SAVAGE
source "drivers/gpu/drm/exynos/Kconfig"
+source "drivers/gpu/drm/rockchip/Kconfig"
source "drivers/gpu/drm/vmwgfx/Kconfig"
source "drivers/gpu/drm/gma500/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4a55d59..d03387a 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ obj-$(CONFIG_DRM_VIA) +=via/ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ +obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig new file mode 100644 index 0000000..7146c80 --- /dev/null +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -0,0 +1,19 @@ +config DRM_ROCKCHIP
- tristate "DRM Support for Rockchip"
- depends on DRM && ROCKCHIP_IOMMU
- select ARM_DMA_USE_IOMMU
- select IOMMU_API
- select DRM_KMS_HELPER
- select DRM_KMS_FB_HELPER
- select DRM_PANEL
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
- select VIDEOMODE_HELPERS
- help
Choose this option if you have a Rockchip soc chipset.
This driver provides kernel mode setting and buffer
management to userspace. This driver does not provides
2D or 3D acceleration; acceleration is performed by other
IP found on the SoC.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile new file mode 100644 index 0000000..6e6d468 --- /dev/null +++ b/drivers/gpu/drm/rockchip/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/rockchip
+rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
rockchip_drm_gem.o rockchip_drm_vop.o
+obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c new file mode 100644 index 0000000..94926cb --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -0,0 +1,524 @@ +/*
- Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
- Author:Mark Yao 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 <drm/rockchip_drm.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);
- return arm_iommu_attach_device(dev, mapping);
+}
+void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
struct device *dev)
+{
- arm_iommu_detach_device(drm_dev->dev);
+}
+static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) +{
- struct rockchip_drm_private *private;
- struct dma_iommu_mapping *mapping;
- struct device *dev = drm_dev->dev;
- int ret;
- private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
- if (!private)
return -ENOMEM;
- dev_set_drvdata(drm_dev->dev, dev);
- drm_dev->dev_private = private;
- drm_mode_config_init(drm_dev);
- rockchip_drm_mode_config_init(drm_dev);
- dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
GFP_KERNEL);
- if (!dev->dma_parms) {
ret = -ENOMEM;
goto err_config_cleanup;
- }
- /* TODO(djkurtz): fetch the mapping start/size from somewhere */
- mapping = arm_iommu_create_mapping(&platform_bus_type, 0x10000000,
SZ_1G);
- if (IS_ERR(mapping)) {
ret = PTR_ERR(mapping);
goto err_config_cleanup;
- }
- dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
- dma_set_max_seg_size(dev, 0xffffffffu);
- ret = arm_iommu_attach_device(dev, mapping);
- if (ret)
goto err_release_mapping;
- /* Try to bind all sub drivers. */
- ret = component_bind_all(dev, drm_dev);
- if (ret)
goto err_detach_device;
- /* init kms poll for handling hpd */
- drm_kms_helper_poll_init(drm_dev);
- /*
* enable drm irq mode.
* - with irq_enabled = true, we can use the vblank feature.
*/
- drm_dev->irq_enabled = true;
- /*
* with vblank_disable_allowed = true, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(after drm_vblank_put function is called)
*/
- drm_dev->vblank_disable_allowed = true;
- ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
- if (ret)
goto err_kms_helper_poll_fini;
- rockchip_drm_fbdev_init(drm_dev);
- /* force connectors detection */
- drm_helper_hpd_irq_event(drm_dev);
- return 0;
+err_kms_helper_poll_fini:
- drm_kms_helper_poll_fini(drm_dev);
- component_unbind_all(dev, drm_dev);
+err_detach_device:
- arm_iommu_detach_device(dev);
+err_release_mapping:
- arm_iommu_release_mapping(dev->archdata.mapping);
+err_config_cleanup:
- drm_mode_config_cleanup(drm_dev);
- drm_dev->dev_private = NULL;
- dev_set_drvdata(dev, NULL);
- return ret;
+}
+static int rockchip_drm_unload(struct drm_device *drm_dev) +{
- struct device *dev = drm_dev->dev;
- drm_kms_helper_poll_fini(drm_dev);
- component_unbind_all(dev, drm_dev);
- arm_iommu_detach_device(dev);
- arm_iommu_release_mapping(dev->archdata.mapping);
- drm_mode_config_cleanup(drm_dev);
- drm_dev->dev_private = NULL;
- dev_set_drvdata(dev, NULL);
- return 0;
+}
+static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state) +{
- struct drm_connector *connector;
- drm_modeset_lock_all(dev);
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
int old_dpms = connector->dpms;
if (connector->funcs->dpms)
connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
/* Set the old mode back to the connector for resume */
connector->dpms = old_dpms;
- }
- drm_modeset_unlock_all(dev);
- return 0;
+}
+static int rockchip_drm_resume(struct drm_device *dev) +{
- struct drm_connector *connector;
- drm_modeset_lock_all(dev);
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->funcs->dpms)
connector->funcs->dpms(connector, connector->dpms);
- }
- drm_modeset_unlock_all(dev);
- drm_helper_resume_force_mode(dev);
- return 0;
+}
+void rockchip_drm_lastclose(struct drm_device *dev) +{
- struct rockchip_drm_private *priv = dev->dev_private;
- drm_modeset_lock_all(dev);
- if (priv->fb_helper)
drm_fb_helper_restore_fbdev_mode(priv->fb_helper);
- drm_modeset_unlock_all(dev);
+}
+static const struct drm_ioctl_desc rockchip_ioctls[] = {
- DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl,
DRM_UNLOCKED | DRM_AUTH),
- DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET, rockchip_gem_get_ioctl,
DRM_UNLOCKED),
- DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
rockchip_gem_map_offset_ioctl, DRM_UNLOCKED |
DRM_AUTH),
+};
+static const struct file_operations rockchip_drm_driver_fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .mmap = rockchip_drm_gem_mmap,
- .poll = drm_poll,
- .read = drm_read,
- .unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
- .compat_ioctl = drm_compat_ioctl,
+#endif
- .release = drm_release,
+};
+const struct vm_operations_struct rockchip_drm_vm_ops = {
- .open = drm_gem_vm_open,
- .close = drm_gem_vm_close,
+};
+static struct drm_driver rockchip_drm_driver = {
- .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
- .load = rockchip_drm_load,
- .unload = rockchip_drm_unload,
- .lastclose = rockchip_drm_lastclose,
- .suspend = rockchip_drm_suspend,
- .resume = rockchip_drm_resume,
- .get_vblank_counter = drm_vblank_count,
- .enable_vblank = rockchip_drm_crtc_enable_vblank,
- .disable_vblank = rockchip_drm_crtc_disable_vblank,
- .gem_vm_ops = &rockchip_drm_vm_ops,
- .gem_free_object = rockchip_gem_free_object,
- .dumb_create = rockchip_gem_dumb_create,
- .dumb_map_offset = rockchip_gem_dumb_map_offset,
- .dumb_destroy = drm_gem_dumb_destroy,
- .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
- .gem_prime_import = drm_gem_prime_import,
- .gem_prime_export = drm_gem_prime_export,
- .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
- .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table,
- .gem_prime_vmap = rockchip_gem_prime_vmap,
- .gem_prime_vunmap = rockchip_gem_prime_vunmap,
- .gem_prime_mmap = rockchip_gem_prime_mmap,
- .ioctls = rockchip_ioctls,
- .num_ioctls = ARRAY_SIZE(rockchip_ioctls),
- .fops = &rockchip_drm_driver_fops,
- .name = DRIVER_NAME,
- .desc = DRIVER_DESC,
- .date = DRIVER_DATE,
- .major = DRIVER_MAJOR,
- .minor = DRIVER_MINOR,
+};
+#ifdef CONFIG_PM_SLEEP +static int rockchip_drm_sys_suspend(struct device *dev) +{
- struct drm_device *drm_dev = dev_get_drvdata(dev);
- pm_message_t message;
- if (pm_runtime_suspended(dev))
return 0;
- message.event = PM_EVENT_SUSPEND;
- return rockchip_drm_suspend(drm_dev, message);
+}
+static int rockchip_drm_sys_resume(struct device *dev) +{
- struct drm_device *drm_dev = dev_get_drvdata(dev);
- if (pm_runtime_suspended(dev))
return 0;
- return rockchip_drm_resume(drm_dev);
+} +#endif
+static const struct dev_pm_ops rockchip_drm_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
rockchip_drm_sys_resume)
+};
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
struct device_node *np)
+{
- struct rockchip_drm_private *priv = drm->dev_private;
- struct device_node *port;
- int pipe;
- if (priv->num_pipe >= ROCKCHIP_MAX_CRTC)
return -EINVAL;
- port = of_get_child_by_name(np, "port");
- of_node_put(np);
- if (!port) {
dev_err(drm->dev, "no port node found in %s\n",
np->full_name);
return -ENXIO;
- }
- pipe = priv->num_pipe++;
- crtc->port = port;
- priv->crtc[pipe] = crtc;
- return pipe;
+}
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe) +{
- struct rockchip_drm_private *priv = drm->dev_private;
- priv->num_pipe--;
- of_node_put(priv->crtc[pipe]->port);
- priv->crtc[pipe] = NULL;
+}
+struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe) +{
- struct rockchip_drm_private *priv = drm->dev_private;
- if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe])
return priv->crtc[pipe];
- return NULL;
+}
+/*
- @node: device tree node containing encoder input ports
- @encoder: drm_encoder
- */
+int rockchip_drm_encoder_get_mux_id(struct device_node *node,
struct drm_encoder *encoder)
+{
- struct device_node *ep = NULL;
- struct drm_crtc *crtc = encoder->crtc;
- struct of_endpoint endpoint;
- struct device_node *port;
- int ret;
- if (!node || !crtc)
return -EINVAL;
- do {
ep = of_graph_get_next_endpoint(node, ep);
if (!ep)
break;
port = of_graph_get_remote_port(ep);
of_node_put(port);
if (port == crtc->port) {
ret = of_graph_parse_endpoint(ep, &endpoint);
return ret ? ret : endpoint.id;
}
- } while (ep);
- return -EINVAL;
+}
+static int compare_of(struct device *dev, void *data) +{
- struct device_node *np = data;
- return dev->of_node == np;
+}
+static void rockchip_add_endpoints(struct device *dev,
struct component_match **match,
struct device_node *port)
+{
- struct device_node *ep, *remote;
- for_each_child_of_node(port, ep) {
remote = of_graph_get_remote_port_parent(ep);
if (!remote || !of_device_is_available(remote)) {
of_node_put(remote);
continue;
} else if (!of_device_is_available(remote->parent)) {
dev_warn(dev, "parent device of %s is not available\n",
remote->full_name);
of_node_put(remote);
continue;
}
component_match_add(dev, match, compare_of, remote);
of_node_put(remote);
- }
+}
+static int rockchip_drm_bind(struct device *dev) +{
- struct drm_device *drm;
- int ret;
- drm = drm_dev_alloc(&rockchip_drm_driver, dev);
- if (!drm)
return -ENOMEM;
- ret = drm_dev_set_unique(drm, "%s", dev_name(dev));
- if (ret)
goto err_free;
- ret = drm_dev_register(drm, 0);
- if (ret)
goto err_free;
- dev_set_drvdata(dev, drm);
- return 0;
+err_free:
- drm_dev_unref(drm);
- return ret;
+}
+static void rockchip_drm_unbind(struct device *dev) +{
- struct drm_device *drm = dev_get_drvdata(dev);
- drm_dev_unregister(drm);
- drm_dev_unref(drm);
+}
+static const struct component_master_ops rockchip_drm_ops = {
- .bind = rockchip_drm_bind,
- .unbind = rockchip_drm_unbind,
+};
+static int rockchip_drm_platform_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct component_match *match = NULL;
- struct device_node *np = dev->of_node;
- struct device_node *port;
- int i;
- int ret;
- if (!np)
return -ENODEV;
- /*
* Bind the crtc ports first, so that
* drm_of_find_possible_crtcs called from encoder .bind callbacks
* works as expected.
*/
- for (i = 0;; i++) {
port = of_parse_phandle(np, "ports", i);
if (!port)
break;
component_match_add(dev, &match, compare_of, port->parent);
of_node_put(port);
- }
- if (i == 0) {
dev_err(dev, "missing 'ports' property\n");
return -ENODEV;
- }
- /*
* For each bound crtc, bind the encoders attached to its
* remote endpoint.
*/
- for (i = 0;; i++) {
port = of_parse_phandle(np, "ports", i);
if (!port)
break;
rockchip_add_endpoints(dev, &match, port);
of_node_put(port);
- }
- ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
- if (ret)
return ret;
- return component_master_add_with_match(dev, &rockchip_drm_ops, match);
+}
+static int rockchip_drm_platform_remove(struct platform_device *pdev) +{
- component_master_del(&pdev->dev, &rockchip_drm_ops);
- return 0;
+}
+static const struct of_device_id rockchip_drm_dt_ids[] = {
- { .compatible = "rockchip,display-subsystem", },
- { /* sentinel */ },
+}; +MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
+static struct platform_driver rockchip_drm_platform_driver = {
- .probe = rockchip_drm_platform_probe,
- .remove = rockchip_drm_platform_remove,
- .driver = {
.owner = THIS_MODULE,
.name = "rockchip-drm",
.of_match_table = rockchip_drm_dt_ids,
- },
+};
+module_platform_driver(rockchip_drm_platform_driver);
+MODULE_AUTHOR("Mark Yao 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..154b3ec --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -0,0 +1,120 @@ +/*
- Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
- Author:Mark Yao 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 4 +#define ROCKCHIP_MAX_CONNECTOR 2
+struct drm_device; +struct drm_connector;
+/*
- display output interface supported by rockchip lcdc
- */
+#define ROCKCHIP_OUTFACE_P888 0 +#define ROCKCHIP_OUTFACE_P666 1 +#define ROCKCHIP_OUTFACE_P565 2 +/* for use special outface */ +#define ROCKCHIP_OUTFACE_AAAA 15
+#define ROCKCHIP_COLOR_SWAP_RG 0x1 +#define ROCKCHIP_COLOR_SWAP_RB 0x2 +#define ROCKCHIP_COLOR_SWAP_GB 0x4
+/*
- Special mode info for rockchip
- @out_type: lcd controller need to know the sceen type.
- */
+struct rockchip_display_mode {
- int out_type;
+};
+#define ROCKCHIP_EVENT_HOTPLUG 1
+enum rockchip_plane_type {
- ROCKCHIP_WIN0,
- ROCKCHIP_WIN1,
- ROCKCHIP_WIN2,
- ROCKCHIP_WIN3,
- ROCKCHIP_CURSOR,
- ROCKCHIP_MAX_PLANE,
+};
+/* This enumerates device type. */ +enum rockchip_drm_device_type {
- ROCKCHIP_DEVICE_TYPE_NONE,
- ROCKCHIP_DEVICE_TYPE_CRTC,
- ROCKCHIP_DEVICE_TYPE_CONNECTOR,
+};
+/* this enumerates display type. */ +enum rockchip_drm_output_type {
- ROCKCHIP_DISPLAY_TYPE_NONE = 0,
- /* RGB Interface. */
- ROCKCHIP_DISPLAY_TYPE_RGB,
- /* LVDS Interface. */
- ROCKCHIP_DISPLAY_TYPE_LVDS,
- /* EDP Interface. */
- ROCKCHIP_DISPLAY_TYPE_EDP,
- /* MIPI Interface. */
- ROCKCHIP_DISPLAY_TYPE_MIPI,
- /* HDMI Interface. */
- ROCKCHIP_DISPLAY_TYPE_HDMI,
+};
+enum rockchip_crtc_type {
- ROCKCHIP_CRTC_VOPB,
- ROCKCHIP_CRTC_VOPL,
- ROCKCHIP_MAX_CRTC,
+};
+/*
- Rockchip drm private structure.
- @num_pipe: number of pipes for this device.
- */
+struct rockchip_drm_private {
- struct drm_fb_helper *fb_helper;
- /*
* created crtc object would be contained at this array and
* this array is used to be aware of which crtc did it request vblank.
*/
- struct drm_crtc *crtc[ROCKCHIP_MAX_CRTC];
- unsigned int num_pipe;
+};
+int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
struct device_node *port);
+void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe); +struct drm_crtc *rockchip_find_crtc(struct drm_device *drm, int pipe); +int rockchip_drm_encoder_get_mux_id(struct device_node *node,
struct drm_encoder *encoder);
+void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe); +void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev); +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe); +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe); +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
struct device *dev);
+void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
struct device *dev);
+#endif /* _ROCKCHIP_DRM_DRV_H_ */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c new file mode 100644 index 0000000..b319505 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -0,0 +1,201 @@ +/*
- Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
- Author:Mark Yao 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/drmP.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_crtc_helper.h> +#include <uapi/drm/rockchip_drm.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..fe1bb22 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c @@ -0,0 +1,231 @@ +/*
- Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
- Author:Mark Yao 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/drmP.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_crtc_helper.h>
+#include <drm/rockchip_drm.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);
- if (!fbdev)
return -ENOMEM;
- fbdev->helper.funcs = &rockchip_drm_fb_helper_funcs;
- helper = &fbdev->helper;
- ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
- if (ret < 0) {
dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
goto err_free;
- }
- ret = drm_fb_helper_single_add_all_connectors(helper);
- if (ret < 0) {
dev_err(dev->dev, "Failed to add connectors.\n");
goto err_drm_fb_helper_fini;
- }
- /* disable all the possible outputs/crtcs before entering KMS mode */
- drm_helper_disable_unused_functions(dev);
- ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
- if (ret < 0) {
dev_err(dev->dev, "Failed to set initial hw configuration.\n");
goto err_drm_fb_helper_fini;
- }
- private->fb_helper = helper;
- return 0;
+err_drm_fb_helper_fini:
- drm_fb_helper_fini(helper);
+err_free:
- kfree(fbdev);
- return ret;
+}
+void rockchip_drm_fbdev_fini(struct drm_device *dev) +{
- struct rockchip_drm_private *private = dev->dev_private;
- struct drm_fb_helper *helper;
- struct rockchip_fbdev *fbdev;
- if (!private || !private->fb_helper)
return;
- helper = private->fb_helper;
- fbdev = to_rockchip_fbdev(helper);
- if (helper->fbdev) {
struct fb_info *info;
int ret;
info = helper->fbdev;
ret = unregister_framebuffer(info);
if (ret < 0)
DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
if (info->cmap.len)
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
- }
- if (helper->fb)
drm_framebuffer_unreference(helper->fb);
- drm_fb_helper_fini(helper);
- kfree(fbdev);
- private->fb_helper = NULL;
+} diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h new file mode 100644 index 0000000..5edcf6a --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h @@ -0,0 +1,20 @@ +/*
- Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
- Author:Mark Yao 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..2f34e92 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -0,0 +1,404 @@ +/*
- Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
- Author:Mark Yao 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/drmP.h> +#include <drm/drm_vma_manager.h> +#include <drm/rockchip_drm.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;
- ret = drm_gem_create_mmap_offset(obj);
- if (ret)
goto err_free_obj;
- ret = rockchip_gem_alloc_buf(rk_obj);
- if (ret)
goto err_free_mmap_offset;
- return rk_obj;
+err_free_mmap_offset:
- drm_gem_free_mmap_offset(obj);
+err_free_obj:
- drm_gem_object_release(obj);
+err_free_rk_obj:
- kfree(rk_obj);
- return ERR_PTR(ret);
+}
+/*
- rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
- function
- */
+void rockchip_gem_free_object(struct drm_gem_object *obj) +{
- struct rockchip_gem_object *rk_obj;
- drm_gem_free_mmap_offset(obj);
- rk_obj = to_rockchip_obj(obj);
- rockchip_gem_free_buf(rk_obj);
- drm_gem_free_mmap_offset(obj);
- drm_gem_object_release(obj);
- kfree(rk_obj);
+}
+int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) +{
- struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
- struct drm_device *drm = obj->dev;
- unsigned long vm_size;
- vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
- vm_size = vma->vm_end - vma->vm_start;
- if (vm_size > obj->size)
return -EINVAL;
- return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
obj->size, &rk_obj->dma_attrs);
+}
+/*
- rockchip_gem_create_with_handle - allocate an object with the given
- size and create a gem handle on it
- returns a struct rockchip_gem_object* on success or ERR_PTR values
- on failure.
- */
+static struct rockchip_gem_object * +rockchip_gem_create_with_handle(struct drm_file *file_priv,
struct drm_device *drm, unsigned int size,
unsigned int *handle)
+{
- struct rockchip_gem_object *rk_obj;
- struct drm_gem_object *obj;
- int ret;
- rk_obj = rockchip_gem_create_object(drm, size);
- if (IS_ERR(rk_obj))
return NULL;
- obj = &rk_obj->base;
- /*
* allocate a id of idr table where the obj is registered
* and handle has the id what user can see.
*/
- ret = drm_gem_handle_create(file_priv, obj, handle);
- if (ret)
goto err_handle_create;
- /* drop reference from allocate - handle holds it now. */
- drm_gem_object_unreference_unlocked(obj);
- return rk_obj;
+err_handle_create:
- rockchip_gem_free_object(obj);
- return ERR_PTR(ret);
+}
+int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
struct drm_device *dev, uint32_t handle,
uint64_t *offset)
+{
- struct drm_gem_object *obj;
- int ret = 0;
- mutex_lock(&dev->struct_mutex);
- /*
* get offset of memory allocated for drm framebuffer.
* - this callback would be called by user application
* with DRM_IOCTL_MODE_MAP_DUMB command.
*/
- obj = drm_gem_object_lookup(dev, file_priv, handle);
- if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
ret = -EINVAL;
goto unlock;
- }
- ret = drm_gem_create_mmap_offset(obj);
- if (ret)
goto out;
- *offset = drm_vma_node_offset_addr(&obj->vma_node);
- DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
+out:
- drm_gem_object_unreference(obj);
+unlock:
- mutex_unlock(&dev->struct_mutex);
- return ret;
+}
+/*
- rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
- function
- This aligns the pitch and size arguments to the minimum required. wrap
- this into your own function if you need bigger alignment.
- */
+int rockchip_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
+{
- struct rockchip_gem_object *rk_obj;
- int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
- if (args->pitch < min_pitch)
args->pitch = min_pitch;
- if (args->size < args->pitch * args->height)
args->size = args->pitch * args->height;
- rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
&args->handle);
- return PTR_ERR_OR_ZERO(rk_obj);
+}
+int rockchip_gem_get_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
+{
- struct drm_rockchip_gem_info *args = data;
- struct rockchip_gem_object *rk_obj;
- struct drm_gem_object *obj;
- mutex_lock(&dev->struct_mutex);
- obj = drm_gem_object_lookup(dev, file_priv, args->handle);
- if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
- }
- rk_obj = to_rockchip_obj(obj);
- args->flags = rk_obj->flags;
- args->size = obj->size;
- drm_gem_object_unreference(obj);
- mutex_unlock(&dev->struct_mutex);
- return 0;
+}
+int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
struct drm_file *file_priv)
+{
- struct drm_rockchip_gem_map_off *args = data;
- return rockchip_gem_dumb_map_offset(file_priv, drm, args->handle,
&args->offset);
+}
+int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
+{
- struct drm_rockchip_gem_create *args = data;
- struct rockchip_gem_object *rk_obj;
- rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
&args->handle);
- return PTR_ERR_OR_ZERO(rk_obj);
+}
+/*
- Allocate a sg_table for this GEM object.
- Note: Both the table's contents, and the sg_table itself must be freed by
the caller.
- Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
- */
+struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj) +{
- struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
- struct drm_device *drm = obj->dev;
- struct sg_table *sgt = NULL;
- int ret;
- sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
- if (!sgt)
return ERR_PTR(-ENOMEM);
- ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
rk_obj->dma_addr, obj->size,
&rk_obj->dma_attrs);
- if (ret) {
DRM_ERROR("failed to allocate sgt, %d\n", ret);
kfree(sgt);
return ERR_PTR(ret);
- }
- return sgt;
+}
+struct drm_gem_object * +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
struct sg_table *sgt)
+{
- struct rockchip_gem_object *rk_obj;
- if (sgt->nents != 1)
return ERR_PTR(-EINVAL);
- rk_obj = rockchip_gem_create_object(dev, size);
- if (IS_ERR(rk_obj))
return ERR_PTR(-ENOMEM);
- return &rk_obj->base;
+}
+void *rockchip_gem_prime_vmap(struct drm_gem_object *obj) +{
- struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
- return rk_obj->kvaddr;
+}
+void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) +{
- /* Nothing to do */
+}
+int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
struct vm_area_struct *vma)
+{
- struct drm_device *dev = obj->dev;
- int ret;
- mutex_lock(&dev->struct_mutex);
- ret = drm_gem_mmap_obj(obj, obj->size, vma);
- mutex_unlock(&dev->struct_mutex);
- return ret;
+} diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h new file mode 100644 index 0000000..6277dbd --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h @@ -0,0 +1,72 @@ +/*
- Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
- Author:Mark Yao 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);
+int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
struct drm_file *file_priv);
+/*
- request gem object creation and buffer allocation as the size
- that it is calculated with framebuffer information such as width,
- height and bpp.
- */
+int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+/* get buffer offset to map to user space. */ +int rockchip_gem_map_offset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+/* get buffer information to memory region allocated by gem. */ +int rockchip_gem_get_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+#endif /* _ROCKCHIP_DRM_GEM_H */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c new file mode 100644 index 0000000..d2ec4d5 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -0,0 +1,1372 @@ +/*
- Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
- Author:Mark Yao 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/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 <drm/rockchip_drm.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_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;
- struct vop_context *ctx;
- 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;
- uint32_t pixclock;
- 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 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),
- .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);
- 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 */ +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 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 vop_clk_enable(struct vop_context *ctx) +{
- int ret;
- if (!ctx->clk_on) {
ret = clk_prepare_enable(ctx->hclk);
if (ret < 0) {
dev_err(ctx->dev, "failed to enable hclk\n");
return ret;
}
ret = clk_prepare_enable(ctx->dclk);
if (ret < 0) {
dev_err(ctx->dev, "failed to enable dclk\n");
goto err_dclk;
}
ret = clk_prepare_enable(ctx->aclk);
if (ret < 0) {
dev_err(ctx->dev, "failed to enable aclk\n");
goto err_aclk;
}
ctx->clk_on = true;
- }
- return ret;
+err_aclk:
- clk_disable_unprepare(ctx->aclk);
+err_dclk:
- clk_disable_unprepare(ctx->hclk);
- return ret;
+}
+static void vop_clk_disable(struct vop_context *ctx) +{
- if (ctx->clk_on) {
clk_disable_unprepare(ctx->dclk);
clk_disable_unprepare(ctx->hclk);
clk_disable_unprepare(ctx->aclk);
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;
- if (!win) {
DRM_ERROR("can't find win data for vop, failed\n");
return -EINVAL;
- }
- 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;
- if (yrgb_mst <= 0)
return -ENOMEM;
- 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 (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;
- }
- 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);
- is_alpha = is_alpha_support(fb->pixel_format);
- format = vop_convert_format(fb->pixel_format);
- 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);
- VOP_WIN_SET(ctx, win, act_info,
((actual_h - 1) << 16) | (actual_w - 1));
- VOP_WIN_SET(ctx, win, dsp_info,
((actual_h - 1) << 16) | (actual_w - 1));
- VOP_WIN_SET(ctx, win, dsp_st, (dsp_sty << 16) | dsp_stx);
- 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);
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 int rockchip_disable_plane(struct drm_plane *plane) +{
- struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
- struct vop_context *ctx = rockchip_plane->ctx;
- const struct vop_win *win = rockchip_plane->win;
- 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 = rockchip_plane->ctx;
- 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,
+};
+struct drm_plane *rockchip_plane_init(struct vop_context *ctx,
unsigned long possible_crtcs,
enum drm_plane_type type)
+{
- struct rockchip_plane *rockchip_plane;
- struct vop_driver_data *vop_data = ctx->data;
- const struct vop_win *win;
- int i;
- int err;
- rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
- if (!rockchip_plane)
return NULL;
- for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++) {
if (!(ctx->win_mask & (1 << i))) {
win = vop_data->win[i];
break;
}
- }
- if (VOP_MAX_WIN_SUPPORT == i) {
DRM_ERROR("failed to find win\n");
kfree(rockchip_plane);
return NULL;
- }
- ctx->win_mask |= (1 << i);
- rockchip_plane->id = i;
- rockchip_plane->win = win;
- rockchip_plane->ctx = ctx;
- 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 NULL;
- }
- return &rockchip_plane->base;
+}
+int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe) +{
- struct vop_context *ctx = to_vop_ctx(rockchip_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_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)
+{
- /* just do dummy now */
- 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 left_margin = adjusted_mode->htotal - adjusted_mode->hsync_end;
- u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
- u16 upper_margin = adjusted_mode->vtotal - adjusted_mode->vsync_end;
- u16 hdisplay = adjusted_mode->hdisplay;
- u16 vdisplay = adjusted_mode->vdisplay;
- u16 htotal = adjusted_mode->htotal;
- u16 vtotal = adjusted_mode->vtotal;
- struct rockchip_display_mode *priv_mode =
(void *)adjusted_mode->private;
- unsigned long flags;
- int ret;
- uint32_t val;
- /* nothing to do if we haven't set the mode yet */
- if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
return -EINVAL;
- if (!priv_mode) {
DRM_ERROR("fail to found display output type[%d]\n",
priv_mode->out_type);
return -EINVAL;
- }
- ret = rockchip_drm_crtc_mode_set_base(crtc, x, y, fb);
- if (ret)
return ret;
- switch (priv_mode->out_type) {
- case ROCKCHIP_DISPLAY_TYPE_RGB:
- case ROCKCHIP_DISPLAY_TYPE_LVDS:
VOP_CTRL_SET(ctx, rgb_en, 1);
VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_P888);
break;
- case ROCKCHIP_DISPLAY_TYPE_EDP:
VOP_CTRL_SET(ctx, edp_en, 1);
VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
break;
- case ROCKCHIP_DISPLAY_TYPE_HDMI:
VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUTFACE_AAAA);
VOP_CTRL_SET(ctx, hdmi_en, 1);
break;
- default:
DRM_ERROR("unsupport out type[%d]\n", priv_mode->out_type);
return -EINVAL;
- };
- 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 = (hsync_len + left_margin) << 16;
- val |= hsync_len + left_margin + hdisplay;
- 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 = (vsync_len + upper_margin) << 16;
- val |= vsync_len + upper_margin + vdisplay;
- 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(vsync_len + upper_margin + vdisplay));
- spin_unlock_irqrestore(&ctx->irq_lock, flags);
- clk_set_rate(ctx->dclk, adjusted_mode->clock * 1000);
- return 0;
+}
+static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
+{
- unsigned int crtc_w;
- unsigned int crtc_h;
- int ret;
- crtc_w = crtc->primary->fb->width - crtc->x;
- crtc_h = crtc->primary->fb->height - crtc->y;
- ret = rockchip_update_plane(crtc->primary, crtc, crtc->primary->fb, 0,
0, crtc_w, crtc_h, crtc->x, crtc->y, crtc_w,
crtc_h);
- if (ret < 0) {
DRM_ERROR("fail to update plane\n");
return -EINVAL;
- }
- 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;
- unsigned int crtc_w;
- unsigned int crtc_h;
- 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;
- crtc_w = crtc->primary->fb->width - crtc->x;
- crtc_h = crtc->primary->fb->height - crtc->y;
- ret = rockchip_update_plane(crtc->primary, crtc, fb, 0, 0, crtc_w,
crtc_h, crtc->x, crtc->y, crtc_w, crtc_h);
- 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;
+}
+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;
- if (!drm_crtc)
return;
- 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);
+}
+void rockchip_drm_crtc_cancel_pending_flip(struct drm_device *dev) +{
- int i;
- for (i = 0; i < dev->num_crtcs; i++)
rockchip_drm_crtc_finish_pageflip(dev, i);
+}
+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 (rockchip_plane->ctx != 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);
- primary = rockchip_plane_init(ctx, possible_crtcs,
DRM_PLANE_TYPE_PRIMARY);
- if (!primary) {
DRM_ERROR("fail to init primary plane\n");
return -EINVAL;
- }
- for (nr = 1; nr < ROCKCHIP_MAX_PLANE; nr++) {
if (nr == VOP_DEFAULT_CURSOR) {
cursor = rockchip_plane_init(ctx, possible_crtcs,
DRM_PLANE_TYPE_CURSOR);
if (!cursor) {
DRM_ERROR("fail to init cursor plane\n");
return -EINVAL;
}
} else {
struct drm_plane *plane;
plane = rockchip_plane_init(ctx, possible_crtcs,
DRM_PLANE_TYPE_OVERLAY);
if (!plane) {
DRM_ERROR("fail to init overlay plane\n");
return -EINVAL;
}
}
- }
- 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 *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 = vop_clk_enable(ctx);
- if (ret < 0)
return ret;
- /*
* do hclk_reset, reset all vop registers.
*/
- rst = devm_reset_control_get(ctx->dev, "ahb");
- if (IS_ERR(rst)) {
dev_err(ctx->dev, "failed to get ahb reset\n");
return PTR_ERR(rst);
- }
- reset_control_assert(rst);
- usleep_range(10, 20);
- reset_control_deassert(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.
*/
- rst = devm_reset_control_get(ctx->dev, "dclk");
- if (IS_ERR(rst)) {
dev_err(ctx->dev, "failed to get dclk reset\n");
return PTR_ERR(rst);
- }
- reset_control_assert(rst);
- usleep_range(10, 20);
- reset_control_deassert(rst);
- ctx->dpms = DRM_MODE_DPMS_ON;
- return 0;
+}
+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..2343760 --- /dev/null +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -0,0 +1,187 @@ +/*
- 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)
+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 */ diff --git a/include/uapi/drm/rockchip_drm.h b/include/uapi/drm/rockchip_drm.h new file mode 100644 index 0000000..3193360 --- /dev/null +++ b/include/uapi/drm/rockchip_drm.h @@ -0,0 +1,75 @@ +/*
- Copyright (c) Fuzhou Rockchip Electronics Co.Ltd
- Authors:
Mark Yao <yzq@rock-chips.com>
- base on exynos_drm.h
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2 of the License, or (at your
- option) any later version.
- */
+#ifndef _UAPI_ROCKCHIP_DRM_H +#define _UAPI_ROCKCHIP_DRM_H
+#include <drm/drm.h>
+/**
- User-desired buffer creation information structure.
- @size: user-desired memory allocation size.
- @flags: user request for setting memory type or cache attributes.
- @handle: returned a handle to created gem object.
- this handle will be set by gem module of kernel side.
- */
+struct drm_rockchip_gem_create {
- uint64_t size;
- uint32_t flags;
- uint32_t handle;
+};
+/**
- A structure for getting buffer offset.
- @handle: a pointer to gem object created.
- @pad: just padding to be 64-bit aligned.
- @offset: relatived offset value of the memory region allocated.
- this value should be set by user.
- */
+struct drm_rockchip_gem_map_off {
- uint32_t handle;
- uint32_t pad;
- uint64_t offset;
+};
+/**
- A structure to gem information.
- @handle: a handle to gem object created.
- @flags: flag value including memory type and cache attribute and
this value would be set by driver.
- @size: size to memory region allocated by gem and this size would
be set by driver.
- */
+struct drm_rockchip_gem_info {
- uint32_t handle;
- uint32_t flags;
- uint64_t size;
+};
+#define DRM_ROCKCHIP_GEM_CREATE 0x00 +#define DRM_ROCKCHIP_GEM_GET 0x01 +#define DRM_ROCKCHIP_GEM_MAP_OFFSET 0x02
+#define DRM_IOCTL_ROCKCHIP_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_GEM_CREATE, struct drm_rockchip_gem_create)
+#define DRM_IOCTL_ROCKCHIP_GEM_MAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_GEM_MAP_OFFSET, struct drm_rockchip_gem_map_off)
+#define DRM_IOCTL_ROCKCHIP_GEM_GET DRM_IOWR(DRM_COMMAND_BASE + \
DRM_ROCKCHIP_GEM_GET, struct drm_rockchip_gem_info)
+#endif /* _UAPI_ROCKCHIP_DRM_H */
1.7.9.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel