]> rtime.felk.cvut.cz Git - zynq/linux.git/commitdiff
drm: xlnx: drv: Add Xilinx bridge support
authorHyun Kwon <hyun.kwon@xilinx.com>
Wed, 24 Jan 2018 01:46:33 +0000 (17:46 -0800)
committerMichal Simek <michal.simek@xilinx.com>
Thu, 1 Mar 2018 08:20:03 +0000 (09:20 +0100)
Similar to drm bridge, but this can be used by any DRM driver. There
is no limitation to be used by non DRM drivers as well. No complex
topology is modeled, thus it's assumed that the Xilinx bridge device is
directly attached to client. The client should call Xilinx bridge
functions explicitly where it's needed, as opposed to drm bridge
functions which are called implicitly by DRM core.
One Xlnx bridge can be owned by one driver at a time.

Signed-off-by: Hyun Kwon <hyun.kwon@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
drivers/gpu/drm/xlnx/Kconfig
drivers/gpu/drm/xlnx/Makefile
drivers/gpu/drm/xlnx/xlnx_bridge.c [new file with mode: 0644]
drivers/gpu/drm/xlnx/xlnx_bridge.h [new file with mode: 0644]
drivers/gpu/drm/xlnx/xlnx_drv.c

index 7c5529c146956abcc9c84b57229237e05179dcbf..09725c83a618e9ecbad68fad552a4a054c9d96f7 100644 (file)
@@ -11,6 +11,15 @@ config DRM_XLNX
          provides the kernel mode setting functionalities
          for Xilinx display drivers.
 
+config DRM_XLNX_BRIDGE
+       tristate "Xilinx DRM KMS bridge"
+       depends on DRM_XLNX
+       help
+         Xilinx DRM KMS bridge. This module provides some interfaces
+         to enable inter-module communication. Choose this option
+         from the provider driver when the Xilinx bridge interface is
+         needed.
+
 config DRM_ZYNQMP_DPSUB
        tristate "ZynqMP DP Subsystem Driver"
        depends on ARCH_ZYNQMP && OF && DRM_XLNX && COMMON_CLK
index 064a05a0ed369d5213d0291a0b459e294daad477..1fe4107f2edd7e4baaa8ad7e9b3ee7288ccb261a 100644 (file)
@@ -1,4 +1,5 @@
 xlnx_drm-objs += xlnx_crtc.o xlnx_drv.o xlnx_fb.o xlnx_gem.o
+xlnx_drm-$(CONFIG_DRM_XLNX_BRIDGE) += xlnx_bridge.o
 obj-$(CONFIG_DRM_XLNX) += xlnx_drm.o
 
 zynqmp-dpsub-objs += zynqmp_disp.o zynqmp_dpsub.o zynqmp_dp.o
diff --git a/drivers/gpu/drm/xlnx/xlnx_bridge.c b/drivers/gpu/drm/xlnx/xlnx_bridge.c
new file mode 100644 (file)
index 0000000..3759d1d
--- /dev/null
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx DRM bridge driver
+ *
+ *  Copyright (C) 2017 Xilinx, Inc.
+ *
+ *  Author: Hyun Woo Kwon <hyun.kwon@xilinx.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 <linux/list.h>
+
+#include "xlnx_bridge.h"
+#include "xlnx_drv.h"
+
+/*
+ * Overview
+ * --------
+ *
+ * Similar to drm bridge, but this can be used by any DRM driver. There
+ * is no limitation to be used by non DRM drivers as well. No complex topology
+ * is modeled, thus it's assumed that the Xilinx bridge device is directly
+ * attached to client. The client should call Xilinx bridge functions explicitly
+ * where it's needed, as opposed to drm bridge functions which are called
+ * implicitly by DRM core.
+ * One Xlnx bridge can be owned by one driver at a time.
+ */
+
+/**
+ * struct xlnx_bridge_helper - Xilinx bridge helper
+ * @xlnx_bridges: list of Xilinx bridges
+ * @lock: lock to protect @xlnx_crtcs
+ * @refcnt: reference count
+ * @error: flag if in error state
+ */
+struct xlnx_bridge_helper {
+       struct list_head xlnx_bridges;
+       struct mutex lock; /* lock for @xlnx_bridges */
+       unsigned int refcnt;
+       bool error;
+};
+
+static struct xlnx_bridge_helper helper;
+
+/*
+ * Client functions
+ */
+
+/**
+ * xlnx_bridge_enable - Enable the bridge
+ * @bridge: bridge to enable
+ *
+ * Enable bridge.
+ *
+ * Return: 0 on success. -ENOENT if no callback, -EFAULT in error state,
+ * or return code from callback.
+ */
+int xlnx_bridge_enable(struct xlnx_bridge *bridge)
+{
+       if (!bridge)
+               return 0;
+
+       if (helper.error)
+               return -EFAULT;
+
+       if (bridge->enable)
+               return bridge->enable(bridge);
+
+       return -ENOENT;
+}
+EXPORT_SYMBOL(xlnx_bridge_enable);
+
+/**
+ * xlnx_bridge_disable - Disable the bridge
+ * @bridge: bridge to disable
+ *
+ * Disable bridge.
+ */
+void xlnx_bridge_disable(struct xlnx_bridge *bridge)
+{
+       if (!bridge)
+               return;
+
+       if (helper.error)
+               return;
+
+       if (bridge->disable)
+               bridge->disable(bridge);
+}
+EXPORT_SYMBOL(xlnx_bridge_disable);
+
+/**
+ * xlnx_bridge_set_input - Set the input of @bridge
+ * @bridge: bridge to set
+ * @width: width
+ * @height: height
+ * @bus_fmt: bus format (ex, MEDIA_BUS_FMT_*);
+ *
+ * Set the bridge input with height / width / format.
+ *
+ * Return: 0 on success. -ENOENT if no callback, -EFAULT if in error state,
+ * or return code from callback.
+ */
+int xlnx_bridge_set_input(struct xlnx_bridge *bridge,
+                         u32 width, u32 height, u32 bus_fmt)
+{
+       if (!bridge)
+               return 0;
+
+       if (helper.error)
+               return -EFAULT;
+
+       if (bridge->set_input)
+               return bridge->set_input(bridge, width, height, bus_fmt);
+
+       return -ENOENT;
+}
+EXPORT_SYMBOL(xlnx_bridge_set_input);
+
+/**
+ * xlnx_bridge_get_input_fmts - Get the supported input formats
+ * @bridge: bridge to set
+ * @fmts: pointer to formats
+ * @count: pointer to format count
+ *
+ * Get the list of supported input bus formats.
+ *
+ * Return: 0 on success. -ENOENT if no callback, -EFAULT if in error state,
+ * or return code from callback.
+ */
+int xlnx_bridge_get_input_fmts(struct xlnx_bridge *bridge,
+                              const u32 **fmts, u32 *count)
+{
+       if (!bridge)
+               return 0;
+
+       if (helper.error)
+               return -EFAULT;
+
+       if (bridge->get_input_fmts)
+               return bridge->get_input_fmts(bridge, fmts, count);
+
+       return -ENOENT;
+}
+EXPORT_SYMBOL(xlnx_bridge_get_input_fmts);
+
+/**
+ * of_xlnx_bridge_get - Get the corresponding Xlnx bridge instance
+ * @bridge_np: The device node of the bridge device
+ *
+ * The function walks through the Xlnx bridge list of @drm, and return
+ * if any registered bridge matches the device node. The returned
+ * bridge will not be accesible by others.
+ *
+ * Return: the matching Xlnx bridge instance, or NULL
+ */
+struct xlnx_bridge *of_xlnx_bridge_get(struct device_node *bridge_np)
+{
+       struct xlnx_bridge *found = NULL;
+       struct xlnx_bridge *bridge;
+
+       if (helper.error)
+               return NULL;
+
+       mutex_lock(&helper.lock);
+       list_for_each_entry(bridge, &helper.xlnx_bridges, list) {
+               if (bridge->of_node == bridge_np && !bridge->owned) {
+                       found = bridge;
+                       bridge->owned = true;
+                       break;
+               }
+       }
+       mutex_unlock(&helper.lock);
+
+       return found;
+}
+EXPORT_SYMBOL_GPL(of_xlnx_bridge_get);
+
+/**
+ * of_xlnx_bridge_put - Put the Xlnx bridge instance
+ * @bridge: Xlnx bridge instance to release
+ *
+ * Return the @bridge. After this, the bridge will be available for
+ * other drivers to use.
+ */
+void of_xlnx_bridge_put(struct xlnx_bridge *bridge)
+{
+       if (WARN_ON(helper.error))
+               return;
+
+       mutex_lock(&helper.lock);
+       WARN_ON(!bridge->owned);
+       bridge->owned = false;
+       mutex_unlock(&helper.lock);
+}
+EXPORT_SYMBOL_GPL(of_xlnx_bridge_put);
+
+/*
+ * Provider functions
+ */
+
+/**
+ * xlnx_bridge_register - Register the bridge instance
+ * @bridge: Xlnx bridge instance to register
+ *
+ * Register @bridge to be available for clients.
+ *
+ * Return: 0 on success. -EPROBE_DEFER if helper is not initialized, or
+ * -EFAULT if in error state.
+ */
+int xlnx_bridge_register(struct xlnx_bridge *bridge)
+{
+       if (!helper.refcnt)
+               return -EPROBE_DEFER;
+
+       if (helper.error)
+               return -EFAULT;
+
+       mutex_lock(&helper.lock);
+       WARN_ON(!bridge->of_node);
+       bridge->owned = false;
+       list_add_tail(&bridge->list, &helper.xlnx_bridges);
+       mutex_unlock(&helper.lock);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xlnx_bridge_register);
+
+/**
+ * xlnx_bridge_unregister - Unregister the bridge instance
+ * @bridge: Xlnx bridge instance to unregister
+ *
+ * Unregister @bridge. The bridge shouldn't be owned by any client
+ * at this point.
+ */
+void xlnx_bridge_unregister(struct xlnx_bridge *bridge)
+{
+       if (helper.error)
+               return;
+
+       mutex_lock(&helper.lock);
+       WARN_ON(bridge->owned);
+       list_del(&bridge->list);
+       mutex_unlock(&helper.lock);
+}
+EXPORT_SYMBOL_GPL(xlnx_bridge_unregister);
+
+/*
+ * Internal functions: used by Xlnx DRM
+ */
+
+/**
+ * xlnx_bridge_helper_init - Initialize the bridge helper
+ * @void: No arg
+ *
+ * Initialize the bridge helper or increment the reference count
+ * if already initialized.
+ *
+ * Return: 0 on success, or -EFAULT if in error state.
+ */
+int xlnx_bridge_helper_init(void)
+{
+       if (helper.refcnt++ > 0) {
+               if (helper.error)
+                       return -EFAULT;
+               return 0;
+       }
+
+       INIT_LIST_HEAD(&helper.xlnx_bridges);
+       mutex_init(&helper.lock);
+       helper.error = false;
+
+       return 0;
+}
+
+/**
+ * xlnx_bridge_helper_fini - Release the bridge helper
+ *
+ * Clean up or decrement the reference of the bridge helper.
+ */
+void xlnx_bridge_helper_fini(void)
+{
+       if (--helper.refcnt > 0)
+               return;
+
+       xlnx_bridge_debugfs_fini();
+
+       if (WARN_ON(!list_empty(&helper.xlnx_bridges))) {
+               helper.error = true;
+               pr_err("any further xlnx bridge call will fail\n");
+       }
+
+       mutex_destroy(&helper.lock);
+}
diff --git a/drivers/gpu/drm/xlnx/xlnx_bridge.h b/drivers/gpu/drm/xlnx/xlnx_bridge.h
new file mode 100644 (file)
index 0000000..27cde3e
--- /dev/null
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Xilinx DRM bridge header
+ *
+ *  Copyright (C) 2017 Xilinx, Inc.
+ *
+ *  Author: Hyun Woo Kwon <hyun.kwon@xilinx.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 _XLNX_BRIDGE_H_
+#define _XLNX_BRIDGE_H_
+
+/**
+ * struct xlnx_bridge - Xilinx bridge device
+ * @list: list node for Xilinx bridge device list
+ * @of_node: OF node for the bridge
+ * @owned: flag if the bridge is owned
+ * @enable: callback to enable the bridge
+ * @disable: callback to disable the bridge
+ * @set_input: callback to set the input
+ * @get_input_fmts: callback to get supported input formats.
+ */
+struct xlnx_bridge {
+       struct list_head list;
+       struct device_node *of_node;
+       bool owned;
+       int (*enable)(struct xlnx_bridge *bridge);
+       void (*disable)(struct xlnx_bridge *bridge);
+       int (*set_input)(struct xlnx_bridge *bridge,
+                        u32 width, u32 height, u32 bus_fmt);
+       int (*get_input_fmts)(struct xlnx_bridge *bridge,
+                             const u32 **fmts, u32 *count);
+};
+
+#if IS_ENABLED(CONFIG_DRM_XLNX_BRIDGE)
+/*
+ * Helper functions: used within Xlnx DRM
+ */
+
+struct xlnx_bridge_helper;
+
+int xlnx_bridge_helper_init(void);
+void xlnx_bridge_helper_fini(void);
+
+/*
+ * Helper functions: used by client driver
+ */
+
+int xlnx_bridge_enable(struct xlnx_bridge *bridge);
+void xlnx_bridge_disable(struct xlnx_bridge *bridge);
+int xlnx_bridge_set_input(struct xlnx_bridge *bridge,
+                         u32 width, u32 height, u32 bus_fmt);
+int xlnx_bridge_get_input_fmts(struct xlnx_bridge *bridge,
+                              const u32 **fmts, u32 *count);
+struct xlnx_bridge *of_xlnx_bridge_get(struct device_node *bridge_np);
+void of_xlnx_bridge_put(struct xlnx_bridge *bridge);
+
+/*
+ * Bridge registration: used by bridge driver
+ */
+
+int xlnx_bridge_register(struct xlnx_bridge *bridge);
+void xlnx_bridge_unregister(struct xlnx_bridge *bridge);
+
+#else /* CONFIG_DRM_XLNX_BRIDGE */
+
+struct xlnx_bridge_helper;
+
+int xlnx_bridge_helper_init(void)
+{
+       return 0;
+}
+
+void xlnx_bridge_helper_fini(void)
+{
+}
+
+int xlnx_bridge_enable(struct xlnx_bridge *bridge)
+{
+       if (bridge)
+               return -ENODEV;
+       return 0;
+}
+
+void xlnx_bridge_disable(struct xlnx_bridge *bridge)
+{
+}
+
+int xlnx_bridge_set_input(struct xlnx_bridge *bridge,
+                         u32 width, u32 height, u32 bus_fmt)
+{
+       if (bridge)
+               return -ENODEV;
+       return 0;
+}
+
+int xlnx_bridge_get_input_fmts(struct xlnx_bridge *bridge,
+                              const u32 **fmts, u32 *count)
+{
+       if (bridge)
+               return -ENODEV;
+       return 0;
+}
+
+struct xlnx_bridge *of_xlnx_bridge_get(struct device_node *bridge_np)
+{
+       return NULL;
+}
+
+void of_xlnx_bridge_put(struct xlnx_bridge *bridge)
+{
+}
+
+void xlnx_bridge_register(struct xlnx_bridge *bridge)
+{
+}
+
+void xlnx_bridge_unregister(struct xlnx_bridge *bridge)
+{
+}
+
+#endif /* CONFIG_DRM_XLNX_BRIDGE */
+
+#endif /* _XLNX_BRIDGE_H_ */
index 667531c5a885df49fce0db976fdda2b5844ef56c..114c01f6b7eb3c6619e165993d9caa280a67e8ca 100644 (file)
@@ -32,7 +32,9 @@
 #include <linux/platform_device.h>
 #include <linux/reservation.h>
 
+#include "xlnx_bridge.h"
 #include "xlnx_crtc.h"
+#include "xlnx_drv.h"
 #include "xlnx_fb.h"
 #include "xlnx_gem.h"
 
@@ -369,6 +371,7 @@ static int xlnx_compare_of(struct device *dev, void *data)
 
 static int xlnx_platform_probe(struct platform_device *pdev)
 {
+       xlnx_bridge_helper_init();
        return xlnx_of_component_probe(&pdev->dev, xlnx_compare_of,
                                       &xlnx_master_ops);
 }
@@ -376,6 +379,7 @@ static int xlnx_platform_probe(struct platform_device *pdev)
 static int xlnx_platform_remove(struct platform_device *pdev)
 {
        component_master_del(&pdev->dev, &xlnx_master_ops);
+       xlnx_bridge_helper_fini();
        return 0;
 }