devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders
@ 2015-04-01 10:09 Heiko Stuebner
  2015-04-01 10:09 ` [PATCH RFC v2 01/12] drm/encoder: add functionality to register encoders to a global list Heiko Stuebner
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied, mark.yao, laurent.pinchart
  Cc: devicetree, dri-devel, linux-rockchip, linux-arm-kernel

changes since v1:
- add separate components subdirectory
- implement the already existing bindings for adv7123 and vga-connector
  instead of defining a new one
- use component graph for subsequent lvds-panel or connected external
  encoders instead of defining special properties.


This series still adds support for the soc-level lvds encoder that also
controls the generic rgb output pins. Also generic components for "dumb"
vga encoders and connectors are added to a new subdirectory which implement
the already existing bindings for adv7123 and vga-connector.

While the major comment from Laurent Pinchart gets addressed with these
separate components, it still needs to address others like the lvds setting
in dt and how to handle the atomic mode setting conversion correctly.

But I'd still like to solicit opinions if the general concept looks sane
especially wrt. introducing the new components element and of_graph structure.

Tested on a firefly board using the vga connector.


Thanks
Heiko


Heiko Stuebner (10):
  drm/encoder: add functionality to register encoders to a global list
  drm/connector: add functionality to register connectors to a global
    list
  drm: add components subdirectory and infrastructure
  drm/components: add generic vga encoder driver
  drm/components: add generic vga connector driver
  drm/rockchip: lvds: register a bridge when no panel is set
  drm/rockchip: enable rgb output of vops for all other connectors
  ARM: dts: rockchip: add rk3288 lcdc0 pinmux settings
  ARM: dts: rockchip: add rk3288 lvds node
  ARM: dts: rockchip: add vga encoder and enable lvds on rk3288-firefly

Mark Yao (2):
  dt-bindings: Add documentation for rockchip lvds
  drm/rockchip: Add support for Rockchip Soc LVDS

 .../devicetree/bindings/video/rockchip-lvds.txt    |  74 ++
 arch/arm/boot/dts/rk3288-firefly.dtsi              |  68 ++
 arch/arm/boot/dts/rk3288.dtsi                      |  51 ++
 drivers/gpu/drm/Kconfig                            |   2 +
 drivers/gpu/drm/Makefile                           |   1 +
 drivers/gpu/drm/components/Kconfig                 |  15 +
 drivers/gpu/drm/components/Makefile                |   4 +
 drivers/gpu/drm/components/vga-connector.c         | 254 ++++++
 drivers/gpu/drm/components/vga-encoder.c           | 315 ++++++++
 drivers/gpu/drm/drm_crtc.c                         |  82 ++
 drivers/gpu/drm/rockchip/Kconfig                   |   9 +
 drivers/gpu/drm/rockchip/Makefile                  |   1 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |  10 +-
 drivers/gpu/drm/rockchip/rockchip_lvds.c           | 851 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.h           | 107 +++
 include/drm/drm_crtc.h                             |  15 +
 16 files changed, 1852 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-lvds.txt
 create mode 100644 drivers/gpu/drm/components/Kconfig
 create mode 100644 drivers/gpu/drm/components/Makefile
 create mode 100644 drivers/gpu/drm/components/vga-connector.c
 create mode 100644 drivers/gpu/drm/components/vga-encoder.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.h

-- 
2.1.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 01/12] drm/encoder: add functionality to register encoders to a global list
  2015-04-01 10:09 [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
@ 2015-04-01 10:09 ` Heiko Stuebner
       [not found] ` <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied, mark.yao, laurent.pinchart
  Cc: devicetree, dri-devel, linux-rockchip, linux-arm-kernel

This allows standalone encoders to be registered and found again
through their devicetree node when going through their connection graph.

Setting the of_node property is of course still optional, as it is not
necessary in most cases to lookup encoders that are part of a bigger
component.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/drm_crtc.c | 41 +++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_crtc.h     |  8 ++++++++
 2 files changed, 49 insertions(+)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index f6d04c7..b63e69d 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -1066,6 +1066,47 @@ void drm_connector_unplug_all(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_connector_unplug_all);
 
+static DEFINE_MUTEX(encoder_lock);
+static LIST_HEAD(encoder_list);
+
+int drm_encoder_add(struct drm_encoder *encoder)
+{
+	mutex_lock(&encoder_lock);
+	list_add_tail(&encoder->list, &encoder_list);
+	mutex_unlock(&encoder_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_encoder_add);
+
+void drm_encoder_remove(struct drm_encoder *encoder)
+{
+	mutex_lock(&encoder_lock);
+	list_del_init(&encoder->list);
+	mutex_unlock(&encoder_lock);
+}
+EXPORT_SYMBOL(drm_encoder_remove);
+
+#ifdef CONFIG_OF
+struct drm_encoder *of_drm_find_encoder(struct device_node *np)
+{
+	struct drm_encoder *encoder;
+
+	mutex_lock(&encoder_lock);
+
+	list_for_each_entry(encoder, &encoder_list, list) {
+		if (encoder->of_node == np) {
+			mutex_unlock(&encoder_lock);
+			return encoder;
+		}
+	}
+
+	mutex_unlock(&encoder_lock);
+	return NULL;
+}
+EXPORT_SYMBOL(of_drm_find_encoder);
+#endif
+
 /**
  * drm_encoder_init - Init a preallocated encoder
  * @dev: drm device
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 920e21a..76994ba 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -569,6 +569,7 @@ struct drm_encoder_funcs {
 /**
  * struct drm_encoder - central DRM encoder structure
  * @dev: parent DRM device
+ * @of_node: device node pointer to the bridge
  * @head: list management
  * @base: base KMS object
  * @name: encoder name
@@ -585,6 +586,10 @@ struct drm_encoder_funcs {
  */
 struct drm_encoder {
 	struct drm_device *dev;
+#ifdef CONFIG_OF
+	struct device_node *of_node;
+#endif
+	struct list_head list;
 	struct list_head head;
 
 	struct drm_mode_object base;
@@ -1225,6 +1230,9 @@ extern void drm_bridge_remove(struct drm_bridge *bridge);
 extern struct drm_bridge *of_drm_find_bridge(struct device_node *np);
 extern int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge);
 
+extern int drm_encoder_add(struct drm_encoder *encoder);
+extern void drm_encoder_remove(struct drm_encoder *encoder);
+extern struct drm_encoder *of_drm_find_encoder(struct device_node *np);
 extern int drm_encoder_init(struct drm_device *dev,
 			    struct drm_encoder *encoder,
 			    const struct drm_encoder_funcs *funcs,
-- 
2.1.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 02/12] drm/connector: add functionality to register connectors to a global list
       [not found] ` <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
@ 2015-04-01 10:09   ` Heiko Stuebner
  2015-04-01 10:09   ` [PATCH RFC v2 03/12] drm: add components subdirectory and infrastructure Heiko Stuebner
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, mark.yao-TNX95d0MmH7DzftRWevZcw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw
  Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w, djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Heiko Stuebner

This allows standalone connectors to be registered and found again
through their devicetree node when going through their connection graph.

Setting the of_node property is of course still optional, as it is not
necessary in most cases to lookup connectors that are part of a bigger
component.

Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
 drivers/gpu/drm/drm_crtc.c | 41 +++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_crtc.h     |  7 +++++++
 2 files changed, 48 insertions(+)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index b63e69d..8b49ea7 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -852,6 +852,47 @@ static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
 		      mode->interlace ?  " interlaced" : "");
 }
 
+static DEFINE_MUTEX(connector_lock);
+static LIST_HEAD(connector_list);
+
+int drm_connector_add(struct drm_connector *connector)
+{
+	mutex_lock(&connector_lock);
+	list_add_tail(&connector->list, &connector_list);
+	mutex_unlock(&connector_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_connector_add);
+
+void drm_connector_remove(struct drm_connector *connector)
+{
+	mutex_lock(&connector_lock);
+	list_del_init(&connector->list);
+	mutex_unlock(&connector_lock);
+}
+EXPORT_SYMBOL(drm_connector_remove);
+
+#ifdef CONFIG_OF
+struct drm_connector *of_drm_find_connector(struct device_node *np)
+{
+	struct drm_connector *connector;
+
+	mutex_lock(&connector_lock);
+
+	list_for_each_entry(connector, &connector_list, list) {
+		if (connector->of_node == np) {
+			mutex_unlock(&connector_lock);
+			return connector;
+		}
+	}
+
+	mutex_unlock(&connector_lock);
+	return NULL;
+}
+EXPORT_SYMBOL(of_drm_find_connector);
+#endif
+
 /**
  * drm_connector_init - Init a preallocated connector
  * @dev: DRM device
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 76994ba..37b230b 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -671,6 +671,10 @@ struct drm_encoder {
  */
 struct drm_connector {
 	struct drm_device *dev;
+#ifdef CONFIG_OF
+	struct device_node *of_node;
+#endif
+	struct list_head list;
 	struct device *kdev;
 	struct device_attribute *attr;
 	struct list_head head;
@@ -1213,6 +1217,9 @@ static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc)
 
 extern void drm_connector_ida_init(void);
 extern void drm_connector_ida_destroy(void);
+extern int drm_connector_add(struct drm_connector *connector);
+extern void drm_connector_remove(struct drm_connector *connector);
+extern struct drm_connector *of_drm_find_connector(struct device_node *np);
 extern int drm_connector_init(struct drm_device *dev,
 			      struct drm_connector *connector,
 			      const struct drm_connector_funcs *funcs,
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 03/12] drm: add components subdirectory and infrastructure
       [not found] ` <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
  2015-04-01 10:09   ` [PATCH RFC v2 02/12] drm/connector: add functionality to register connectors " Heiko Stuebner
@ 2015-04-01 10:09   ` Heiko Stuebner
  2015-04-01 10:09   ` [PATCH RFC v2 05/12] drm/components: add generic vga connector driver Heiko Stuebner
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, mark.yao-TNX95d0MmH7DzftRWevZcw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw
  Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w, djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Heiko Stuebner

Until there are only the specialized bridge and i2c directories available
to hold generic support code like external components. But as there are
also things like external encoders that do not use i2c, those do not match
all cases.
Therefore introduce a new subdirectories to hold generic components like the
mentioned external encoders that can be used by component-based drm drivers.

Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
 drivers/gpu/drm/Kconfig             | 2 ++
 drivers/gpu/drm/Makefile            | 1 +
 drivers/gpu/drm/components/Kconfig  | 4 ++++
 drivers/gpu/drm/components/Makefile | 1 +
 4 files changed, 8 insertions(+)
 create mode 100644 drivers/gpu/drm/components/Kconfig
 create mode 100644 drivers/gpu/drm/components/Makefile

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 151a050..2f454f4 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -81,6 +81,8 @@ source "drivers/gpu/drm/i2c/Kconfig"
 
 source "drivers/gpu/drm/bridge/Kconfig"
 
+source "drivers/gpu/drm/components/Kconfig"
+
 config DRM_TDFX
 	tristate "3dfx Banshee/Voodoo3+"
 	depends on DRM && PCI
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 2c239b9..aa9ccaa 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -69,3 +69,4 @@ obj-$(CONFIG_DRM_IMX) += imx/
 obj-y			+= i2c/
 obj-y			+= panel/
 obj-y			+= bridge/
+obj-y			+= components/
diff --git a/drivers/gpu/drm/components/Kconfig b/drivers/gpu/drm/components/Kconfig
new file mode 100644
index 0000000..9d5d462
--- /dev/null
+++ b/drivers/gpu/drm/components/Kconfig
@@ -0,0 +1,4 @@
+menu "Standalone components for use with the component framework"
+     depends on DRM && DRM_KMS_HELPER
+
+endmenu
diff --git a/drivers/gpu/drm/components/Makefile b/drivers/gpu/drm/components/Makefile
new file mode 100644
index 0000000..be16eca
--- /dev/null
+++ b/drivers/gpu/drm/components/Makefile
@@ -0,0 +1 @@
+ccflags-y := -Iinclude/drm
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 04/12] drm/components: add generic vga encoder driver
  2015-04-01 10:09 [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
  2015-04-01 10:09 ` [PATCH RFC v2 01/12] drm/encoder: add functionality to register encoders to a global list Heiko Stuebner
       [not found] ` <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
@ 2015-04-01 10:09 ` Heiko Stuebner
       [not found]   ` <1427882986-19110-5-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
  2015-04-01 10:09 ` [PATCH RFC v2 06/12] dt-bindings: Add documentation for rockchip lvds Heiko Stuebner
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied, mark.yao, laurent.pinchart
  Cc: devicetree, dri-devel, linux-rockchip, linux-arm-kernel

This adds a driver for generic vga encoders like the Analog Devices adv7123
and similar ics. These chips do not have any special configuration options
except a powersafe gpio.

An exception is added for the rcar-du driver which also implements support
for the adv7123 internally but is not yet converted to the component framework.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/components/Kconfig       |   5 +
 drivers/gpu/drm/components/Makefile      |   2 +
 drivers/gpu/drm/components/vga-encoder.c | 315 +++++++++++++++++++++++++++++++
 3 files changed, 322 insertions(+)
 create mode 100644 drivers/gpu/drm/components/vga-encoder.c

diff --git a/drivers/gpu/drm/components/Kconfig b/drivers/gpu/drm/components/Kconfig
index 9d5d462..647cea6 100644
--- a/drivers/gpu/drm/components/Kconfig
+++ b/drivers/gpu/drm/components/Kconfig
@@ -1,4 +1,9 @@
 menu "Standalone components for use with the component framework"
      depends on DRM && DRM_KMS_HELPER
 
+config DRM_COMPONENTS_VGA_ENCODER
+	tristate "Generic vga encoder"
+	help
+	  Support for generic vga encoder chips without any special controls.
+
 endmenu
diff --git a/drivers/gpu/drm/components/Makefile b/drivers/gpu/drm/components/Makefile
index be16eca..719b1c9 100644
--- a/drivers/gpu/drm/components/Makefile
+++ b/drivers/gpu/drm/components/Makefile
@@ -1 +1,3 @@
 ccflags-y := -Iinclude/drm
+
+obj-$(CONFIG_DRM_COMPONENTS_VGA_ENCODER) += vga-encoder.o
diff --git a/drivers/gpu/drm/components/vga-encoder.c b/drivers/gpu/drm/components/vga-encoder.c
new file mode 100644
index 0000000..f559b5e
--- /dev/null
+++ b/drivers/gpu/drm/components/vga-encoder.c
@@ -0,0 +1,315 @@
+/*
+ * Simple vga encoder driver
+ *
+ * Copyright (C) 2014 Heiko Stuebner <heiko@sntech.de>
+ *
+ * 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/module.h>
+#include <linux/component.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_graph.h>
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+
+#define encoder_to_vga_encoder(x) container_of(x, struct vga_encoder, encoder)
+
+struct vga_encoder {
+	struct drm_encoder encoder;
+	struct device *dev;
+	struct regulator *vaa_reg;
+	struct gpio_desc *psave_gpio;
+
+	struct mutex enable_lock;
+	bool enabled;
+};
+
+static void vga_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs vga_encoder_funcs = {
+	.destroy = vga_encoder_destroy,
+};
+
+static void vga_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct vga_encoder *vga = encoder_to_vga_encoder(encoder);
+
+	mutex_lock(&vga->enable_lock);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		if (vga->enabled)
+			goto out;
+
+		if (!IS_ERR(vga->vaa_reg))
+			regulator_enable(vga->vaa_reg);
+
+		if (vga->psave_gpio)
+			gpiod_set_value(vga->psave_gpio, 1);
+
+		vga->enabled = true;
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		if (!vga->enabled)
+			goto out;
+
+		if (vga->psave_gpio)
+			gpiod_set_value(vga->psave_gpio, 0);
+
+		if (!IS_ERR(vga->vaa_reg))
+			regulator_enable(vga->vaa_reg);
+
+		vga->enabled = false;
+		break;
+	default:
+		break;
+	}
+
+out:
+	mutex_unlock(&vga->enable_lock);
+}
+
+static bool vga_encoder_mode_fixup(struct drm_encoder *encoder,
+				  const struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void vga_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void vga_encoder_mode_set(struct drm_encoder *encoder,
+					struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void vga_encoder_commit(struct drm_encoder *encoder)
+{
+	vga_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void vga_encoder_disable(struct drm_encoder *encoder)
+{
+	vga_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static const struct drm_encoder_helper_funcs vga_encoder_helper_funcs = {
+	.dpms = vga_encoder_dpms,
+	.mode_fixup = vga_encoder_mode_fixup,
+	.prepare = vga_encoder_prepare,
+	.mode_set = vga_encoder_mode_set,
+	.commit = vga_encoder_commit,
+	.disable = vga_encoder_disable,
+};
+
+/*
+ * Component helper functions
+ */
+
+static int vga_encoder_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct vga_encoder *vga = dev_get_drvdata(dev);
+	struct device_node *np = vga->encoder.of_node;
+	struct drm_device *drm_dev = data;
+
+	vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm_dev, np);
+
+	drm_encoder_helper_add(&vga->encoder, &vga_encoder_helper_funcs);
+	drm_encoder_init(drm_dev, &vga->encoder, &vga_encoder_funcs,
+			 DRM_MODE_ENCODER_DAC);
+
+	return component_bind_all(dev, drm_dev);
+}
+
+static void vga_encoder_unbind(struct device *dev, struct device *master,
+				    void *data)
+{
+	struct vga_encoder *vga = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+
+	component_unbind_all(dev, drm_dev);
+	vga->encoder.funcs->destroy(&vga->encoder);
+}
+
+static const struct component_ops vga_encoder_ops = {
+	.bind = vga_encoder_bind,
+	.unbind = vga_encoder_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int vga_encoder_master_bind(struct device *dev)
+{
+	return 0;
+}
+
+static void vga_encoder_master_unbind(struct device *dev)
+{
+	/* do nothing */
+}
+
+static const struct component_master_ops vga_encoder_master_ops = {
+	.bind = vga_encoder_master_bind,
+	.unbind = vga_encoder_master_unbind,
+};
+
+static int vga_encoder_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *port, *connector_node;
+	struct device *dev = &pdev->dev;
+	struct component_match *match = NULL;
+	struct vga_encoder *vga;
+	int ret;
+
+	if (!np)
+		return -ENODEV;
+
+	vga = devm_kzalloc(dev, sizeof(*vga), GFP_KERNEL);
+	if (!vga)
+		return -ENOMEM;
+
+	vga->dev = dev;
+	dev_set_drvdata(dev, vga);
+	mutex_init(&vga->enable_lock);
+
+	vga->psave_gpio = devm_gpiod_get_optional(dev, "psave",
+						   GPIOD_OUT_LOW);
+	if (IS_ERR(vga->psave_gpio)) {
+		ret = PTR_ERR(vga->psave_gpio);
+		dev_err(dev, "failed to request GPIO: %d\n", ret);
+		return ret;
+	}
+
+	vga->enabled = false;
+	vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
+	vga->encoder.of_node = np;
+
+	port = of_graph_get_port_by_id(dev->of_node, 1);
+	if (port) {
+		struct device_node *endpoint;
+
+		endpoint = of_get_child_by_name(port, "endpoint");
+		if (endpoint) {
+			connector_node = of_graph_get_remote_port_parent(endpoint);
+			of_node_put(endpoint);
+		}
+
+		of_node_put(port);
+	}
+
+	if (!of_drm_find_connector(connector_node))
+		return -EPROBE_DEFER;
+
+	component_match_add(dev, &match, compare_of, connector_node);
+
+	ret = drm_encoder_add(&vga->encoder);
+	if (ret < 0)
+		return ret;
+
+	ret = component_master_add_with_match(dev, &vga_encoder_master_ops, match);
+	if (ret < 0)
+		goto err_encoder_remove;
+
+	ret = component_add(dev, &vga_encoder_ops);
+	if (ret < 0)
+		goto err_master_remove;
+
+	return 0;
+
+err_master_remove:
+	component_master_del(&pdev->dev, &vga_encoder_master_ops);
+err_encoder_remove:
+	drm_encoder_remove(&vga->encoder);
+
+	return ret;
+}
+
+static int vga_encoder_remove(struct platform_device *pdev)
+{
+	struct vga_encoder *vga = dev_get_drvdata(&pdev->dev);
+
+	component_del(&pdev->dev, &vga_encoder_ops);
+	component_master_del(&pdev->dev, &vga_encoder_master_ops);
+	drm_encoder_remove(&vga->encoder);
+
+	return 0;
+}
+
+static const struct of_device_id vga_encoder_ids[] = {
+	{ .compatible = "adi,adv7123", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, vga_encoder_ids);
+
+static struct platform_driver vga_encoder_driver = {
+	.probe  = vga_encoder_probe,
+	.remove = vga_encoder_remove,
+	.driver = {
+		.name = "vga-encoder",
+		.of_match_table = vga_encoder_ids,
+	},
+};
+
+static const struct of_device_id rcar_du_of_table[] = {
+	{ .compatible = "renesas,du-r8a7779" },
+	{ .compatible = "renesas,du-r8a7790" },
+	{ .compatible = "renesas,du-r8a7791" },
+	{ }
+};
+
+static int __init vga_encoder_init(void)
+{
+	struct device_node *np;
+
+	/*
+	 * Play nice with rcar-du that is having its own implementation
+	 * of the adv7123 binding implementation and is not yet
+	 * converted to using components.
+	 */
+	np = of_find_matching_node(NULL, rcar_du_of_table);
+	if (np) {
+		of_node_put(np);
+		return 0;
+	}
+
+	return platform_driver_register(&vga_encoder_driver);
+}
+
+static void __exit vga_encoder_exit(void)
+{
+	platform_driver_unregister(&vga_encoder_driver);
+}
+
+module_init(vga_encoder_init);
+module_exit(vga_encoder_exit);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("Simple vga converter");
+MODULE_LICENSE("GPL");
-- 
2.1.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 05/12] drm/components: add generic vga connector driver
       [not found] ` <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
  2015-04-01 10:09   ` [PATCH RFC v2 02/12] drm/connector: add functionality to register connectors " Heiko Stuebner
  2015-04-01 10:09   ` [PATCH RFC v2 03/12] drm: add components subdirectory and infrastructure Heiko Stuebner
@ 2015-04-01 10:09   ` Heiko Stuebner
  2015-04-01 10:09   ` [PATCH RFC v2 07/12] drm/rockchip: Add support for Rockchip Soc LVDS Heiko Stuebner
  2015-04-01 10:09   ` [PATCH RFC v2 08/12] drm/rockchip: lvds: register a bridge when no panel is set Heiko Stuebner
  4 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, mark.yao-TNX95d0MmH7DzftRWevZcw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw
  Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w, djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Heiko Stuebner

This adds a driver for generic vga connectors using a system i2c-bus for ddc.

An exception is included for rcar-du which implements the vga-connector
binding interally already and is not yet converted to the component framework.

Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
 drivers/gpu/drm/components/Kconfig         |   6 +
 drivers/gpu/drm/components/Makefile        |   1 +
 drivers/gpu/drm/components/vga-connector.c | 254 +++++++++++++++++++++++++++++
 3 files changed, 261 insertions(+)
 create mode 100644 drivers/gpu/drm/components/vga-connector.c

diff --git a/drivers/gpu/drm/components/Kconfig b/drivers/gpu/drm/components/Kconfig
index 647cea6..8424143 100644
--- a/drivers/gpu/drm/components/Kconfig
+++ b/drivers/gpu/drm/components/Kconfig
@@ -6,4 +6,10 @@ config DRM_COMPONENTS_VGA_ENCODER
 	help
 	  Support for generic vga encoder chips without any special controls.
 
+config DRM_COMPONENTS_VGA_CONNECTOR:
+	tristate "Generic vga connector"
+	help
+	  Support for simple vga connectors using a system i2c bus
+	  for ddc.
+
 endmenu
diff --git a/drivers/gpu/drm/components/Makefile b/drivers/gpu/drm/components/Makefile
index 719b1c9..2ff64da 100644
--- a/drivers/gpu/drm/components/Makefile
+++ b/drivers/gpu/drm/components/Makefile
@@ -1,3 +1,4 @@
 ccflags-y := -Iinclude/drm
 
 obj-$(CONFIG_DRM_COMPONENTS_VGA_ENCODER) += vga-encoder.o
+obj-$(CONFIG_DRM_COMPONENTS_VGA_CONNECTOR) += vga-connector.o
diff --git a/drivers/gpu/drm/components/vga-connector.c b/drivers/gpu/drm/components/vga-connector.c
new file mode 100644
index 0000000..400ceb7
--- /dev/null
+++ b/drivers/gpu/drm/components/vga-connector.c
@@ -0,0 +1,254 @@
+/*
+ * Simple vga encoder driver
+ *
+ * Copyright (C) 2014 Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
+ *
+ * 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/module.h>
+#include <linux/component.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_graph.h>
+#include <drm/drm_of.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+
+#define connector_to_vga_connector(x) container_of(x, struct vga_connector, connector)
+
+struct vga_connector {
+	struct drm_connector connector;
+	struct device *dev;
+	struct i2c_adapter *ddc;
+	struct drm_encoder *encoder;
+};
+
+enum drm_connector_status vga_connector_detect(struct drm_connector *connector,
+					    bool force)
+{
+	struct vga_connector *vga = connector_to_vga_connector(connector);
+
+	if (!vga->ddc)
+		return connector_status_unknown;
+
+	if (drm_probe_ddc(vga->ddc))
+		return connector_status_connected;
+
+	return connector_status_disconnected;
+}
+
+void vga_connector_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+struct drm_connector_funcs vga_connector_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = vga_connector_detect,
+	.destroy = vga_connector_connector_destroy,
+};
+
+/*
+ * Connector helper functions
+ */
+
+static int vga_connector_connector_get_modes(struct drm_connector *connector)
+{
+	struct vga_connector *vga = connector_to_vga_connector(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	if (!vga->ddc)
+		return 0;
+
+	edid = drm_get_edid(connector, vga->ddc);
+	if (edid) {
+		drm_mode_connector_update_edid_property(connector, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	}
+
+	return ret;
+}
+
+static int vga_connector_connector_mode_valid(struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder
+*vga_connector_connector_best_encoder(struct drm_connector *connector)
+{
+	struct vga_connector *vga = connector_to_vga_connector(connector);
+
+	return vga->encoder;
+}
+
+static struct drm_connector_helper_funcs vga_connector_connector_helper_funcs = {
+	.get_modes = vga_connector_connector_get_modes,
+	.best_encoder = vga_connector_connector_best_encoder,
+	.mode_valid = vga_connector_connector_mode_valid,
+};
+
+
+static int vga_connector_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct vga_connector *vga = dev_get_drvdata(dev);
+	struct drm_device *drm = data;
+	struct device_node *endpoint, *encp = NULL;
+
+	vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
+				DRM_CONNECTOR_POLL_DISCONNECT;
+
+	drm_connector_helper_add(&vga->connector,
+				 &vga_connector_connector_helper_funcs);
+	drm_connector_init(drm, &vga->connector, &vga_connector_connector_funcs,
+			   DRM_MODE_CONNECTOR_VGA);
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (endpoint)
+		encp = of_graph_get_remote_port_parent(endpoint);
+	of_node_put(endpoint);
+
+	if (!encp)
+		return -ENODEV;
+
+	vga->encoder = of_drm_find_encoder(encp);
+	of_node_put(encp);
+
+	if (!vga->encoder)
+		return -EPROBE_DEFER;
+
+	drm_mode_connector_attach_encoder(&vga->connector, vga->encoder);
+
+	return 0;
+}
+
+static void vga_connector_unbind(struct device *dev, struct device *master,
+				    void *data)
+{
+	struct vga_connector *vga = dev_get_drvdata(dev);
+
+	vga->connector.funcs->destroy(&vga->connector);
+}
+
+static const struct component_ops vga_connector_ops = {
+	.bind = vga_connector_bind,
+	.unbind = vga_connector_unbind,
+};
+
+static int vga_connector_probe(struct platform_device *pdev)
+{
+	struct device_node *ddc_node, *np = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	struct vga_connector *vga;
+	int ret;
+
+	if (!np)
+		return -ENODEV;
+
+	vga = devm_kzalloc(dev, sizeof(*vga), GFP_KERNEL);
+	if (!vga)
+		return -ENOMEM;
+
+	vga->dev = dev;
+	vga->connector.of_node = np;
+
+	dev_set_drvdata(dev, vga);
+
+	ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
+	if (ddc_node) {
+		vga->ddc = of_find_i2c_adapter_by_node(ddc_node);
+		of_node_put(ddc_node);
+		if (!vga->ddc) {
+			dev_dbg(vga->dev, "failed to read ddc node\n");
+			return -EPROBE_DEFER;
+		}
+	} else {
+		dev_dbg(vga->dev, "no ddc property found\n");
+	}
+
+	ret = drm_connector_add(&vga->connector);
+	if (ret < 0)
+		return ret;
+
+	return component_add(dev, &vga_connector_ops);
+}
+
+static int vga_connector_remove(struct platform_device *pdev)
+{
+	struct vga_connector *vga = dev_get_drvdata(&pdev->dev);
+
+	component_del(&pdev->dev, &vga_connector_ops);
+	drm_connector_remove(&vga->connector);
+
+	return 0;
+}
+
+static const struct of_device_id vga_connector_ids[] = {
+	{ .compatible = "vga-connector", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, vga_connector_ids);
+
+static struct platform_driver vga_connector_driver = {
+	.probe  = vga_connector_probe,
+	.remove = vga_connector_remove,
+	.driver = {
+		.name = "vga-connector",
+		.of_match_table = vga_connector_ids,
+	},
+};
+
+static const struct of_device_id rcar_du_of_table[] = {
+	{ .compatible = "renesas,du-r8a7779" },
+	{ .compatible = "renesas,du-r8a7790" },
+	{ .compatible = "renesas,du-r8a7791" },
+	{ }
+};
+
+static int __init vga_connector_init(void)
+{
+	struct device_node *np;
+
+	/*
+	 * Play nice with rcar-du that is having its own implementation
+	 * of the vga-connector binding implementation and is not yet
+	 * converted to using components.
+	 */
+	np = of_find_matching_node(NULL, rcar_du_of_table);
+	if (np) {
+		of_node_put(np);
+		return 0;
+	}
+
+	return platform_driver_register(&vga_connector_driver);
+}
+
+static void __exit vga_connector_exit(void)
+{
+	platform_driver_unregister(&vga_connector_driver);
+}
+
+module_init(vga_connector_init);
+module_exit(vga_connector_exit);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>");
+MODULE_DESCRIPTION("Simple vga converter");
+MODULE_LICENSE("GPL");
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 06/12] dt-bindings: Add documentation for rockchip lvds
  2015-04-01 10:09 [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
                   ` (2 preceding siblings ...)
  2015-04-01 10:09 ` [PATCH RFC v2 04/12] drm/components: add generic vga encoder driver Heiko Stuebner
@ 2015-04-01 10:09 ` Heiko Stuebner
  2015-04-01 10:09 ` [PATCH RFC v2 09/12] drm/rockchip: enable rgb output of vops for all other connectors Heiko Stuebner
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied, mark.yao, laurent.pinchart
  Cc: devicetree, Mark Yao, dri-devel, linux-rockchip, linux-arm-kernel

From: Mark Yao <yzq@rock-chips.com>

Add binding documentation for Rockchip SoC LVDS driver.

Signed-off-by: Mark Yao <yzq@rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 .../devicetree/bindings/video/rockchip-lvds.txt    | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-lvds.txt

diff --git a/Documentation/devicetree/bindings/video/rockchip-lvds.txt b/Documentation/devicetree/bindings/video/rockchip-lvds.txt
new file mode 100644
index 0000000..80529f4
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-lvds.txt
@@ -0,0 +1,74 @@
+Rockchip RK3288 LVDS interface
+================================
+
+Required properties:
+- compatible: "rockchip,rk3288-lvds";
+
+- reg: physical base address of the controller and length
+	of memory mapped region.
+- clocks: must include clock specifiers corresponding to entries in the
+	clock-names property.
+- clock-names: must contain "pclk_lvds"
+
+- avdd1v0-supply: regulator phandle for 1.0V analog power
+- avdd1v8-supply: regulator phandle for 1.8V analog power
+- avdd3v3-supply: regulator phandle for 3.3V analog power
+
+- rockchip,grf: phandle to the general register files syscon
+
+- rockchip,data-mapping: should be "vesa" or "jeida",
+	This describes how the color bits are laid out in the
+	serialized LVDS signal.
+- rockchip,data-width : should be <18> or <24>;
+- rockchip,output: should be "rgb", "lvds" or "duallvds",
+	This describes the output face.
+
+Required nodes:
+
+The lvds has two video ports as described by
+	Documentation/devicetree/bindings/media/video-interfaces.txt.
+Their connections are modeled using the OF graph bindings specified in
+	Documentation/devicetree/bindings/graph.txt.
+
+- video port 0 for the VOP inputs
+- video port 1 for either a panel or subsequent encoder
+
+Example:
+	lvds: lvds@ff96c000 {
+		compatible = "rockchip,rk3288-lvds";
+		rockchip,grf = <&grf>;
+		reg = <0xff96c000 0x4000>;
+		clocks = <&cru PCLK_LVDS_PHY>;
+		clock-names = "pclk_lvds";
+		avdd1v0-supply = <&vdd10_lcd>;
+		avdd1v8-supply = <&vcc18_lcd>;
+		avdd3v3-supply = <&vcca_33>;
+		rockchip,data-mapping = "jeida";
+		rockchip,data-width = <24>;
+		rockchip,output = "rgb";
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			lvds_in: port@0 {
+				reg = <0>;
+
+				lvds_in_vopb: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_lvds>;
+				};
+				lvds_in_vopl: endpoint@1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_lvds>;
+				};
+			};
+
+			lvds_out: port@1 {
+				reg = <1>;
+
+				lvds_out_panel: endpoint {
+					remote-endpoint = <&panel_in>;
+				};
+			};
+		};
+	};
-- 
2.1.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 07/12] drm/rockchip: Add support for Rockchip Soc LVDS
       [not found] ` <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
                     ` (2 preceding siblings ...)
  2015-04-01 10:09   ` [PATCH RFC v2 05/12] drm/components: add generic vga connector driver Heiko Stuebner
@ 2015-04-01 10:09   ` Heiko Stuebner
  2015-04-01 10:09   ` [PATCH RFC v2 08/12] drm/rockchip: lvds: register a bridge when no panel is set Heiko Stuebner
  4 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, mark.yao-TNX95d0MmH7DzftRWevZcw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw
  Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w, djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Yao,
	Heiko Stuebner

From: Mark Yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

This adds support for Rockchip soc lvds found on rk3288

Signed-off-by: Mark Yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
This still needs to address Laurent's comment about trying to get the
lvds settings from the panel/connected device instead of encoding them
as separate properties in the devicetree.

 drivers/gpu/drm/rockchip/Kconfig         |   9 +
 drivers/gpu/drm/rockchip/Makefile        |   1 +
 drivers/gpu/drm/rockchip/rockchip_lvds.c | 640 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.h | 107 ++++++
 4 files changed, 757 insertions(+)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 35215f6..845f953 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,3 +25,12 @@ config ROCKCHIP_DW_HDMI
 	  for the Synopsys DesignWare HDMI driver. If you want to
 	  enable HDMI on RK3288 based SoC, you should selet this
 	  option.
+
+config ROCKCHIP_LVDS
+	tristate "Rockchip lvds support"
+	depends on DRM_ROCKCHIP
+	help
+	  Choose this option to enable support for Rockchip LVDS controllers.
+	  Rockchip rk3288 SoC has LVDS TX Controller can be used, and it
+	  support lvds, rgb, dual lvds output mode. say Y to enable its
+	  driver.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index f3d8a19..8541304 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -6,5 +6,6 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
 		rockchip_drm_gem.o
 
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+obj-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
 
 obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o rockchip_drm_vop.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
new file mode 100644
index 0000000..657609e
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * 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_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_of.h>
+
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#include <video/display_timing.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+#include "rockchip_lvds.h"
+
+#define DISPLAY_OUTPUT_RGB		0
+#define DISPLAY_OUTPUT_LVDS		1
+#define DISPLAY_OUTPUT_DUAL_LVDS	2
+
+#define connector_to_lvds(c) \
+		container_of(c, struct rockchip_lvds, connector)
+
+#define encoder_to_lvds(c) \
+		container_of(c, struct rockchip_lvds, encoder)
+
+/*
+ * @grf_offset: offset inside the grf regmap for setting the rockchip lvds
+ */
+struct rockchip_lvds_soc_data {
+	int grf_soc_con6;
+	int grf_soc_con7;
+};
+
+struct rockchip_lvds {
+	void *base;
+	struct device *dev;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct clk *pclk;
+	const struct rockchip_lvds_soc_data *soc_data;
+
+	struct regulator_bulk_data supplies[3];
+
+	int output;
+	int format;
+
+	struct drm_device *drm_dev;
+	struct drm_panel *panel;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	struct mutex suspend_lock;
+	int suspend;
+};
+
+static inline void lvds_writel(struct rockchip_lvds *lvds, u32 offset, u32 val)
+{
+	writel_relaxed(val, lvds->regs + offset);
+	writel_relaxed(val, lvds->regs + offset + 0x100);
+}
+
+static inline int lvds_name_to_format(const char *s)
+{
+	if (!s)
+		return -EINVAL;
+
+	if (strncmp(s, "jeida", 6) == 0)
+		return LVDS_FORMAT_JEIDA;
+	else if (strncmp(s, "vesa", 6) == 0)
+		return LVDS_FORMAT_VESA;
+
+	return -EINVAL;
+}
+
+static inline int lvds_name_to_output(const char *s)
+{
+	if (!s)
+		return -EINVAL;
+
+	if (strncmp(s, "rgb", 3) == 0)
+		return DISPLAY_OUTPUT_RGB;
+	else if (strncmp(s, "lvds", 4) == 0)
+		return DISPLAY_OUTPUT_LVDS;
+	else if (strncmp(s, "duallvds", 8) == 0)
+		return DISPLAY_OUTPUT_DUAL_LVDS;
+
+	return -EINVAL;
+}
+
+static int rockchip_lvds_poweron(struct rockchip_lvds *lvds)
+{
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(lvds->supplies),
+				    lvds->supplies);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_enable(lvds->pclk);
+	if (ret < 0) {
+		dev_err(lvds->dev, "failed to enable lvds pclk %d\n", ret);
+		return ret;
+	}
+
+	writel(RK3288_LVDS_CFG_REGC_PLL_ENABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REGC);
+	writel(RK3288_LVDS_CFG_REG21_TX_ENABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REG21);
+
+	return 0;
+}
+
+static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds)
+{
+	int ret;
+
+	ret = regmap_write(lvds->grf,
+			   lvds->soc_data->grf_soc_con7, 0xffff8000);
+	if (ret != 0)
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+
+	writel(RK3288_LVDS_CFG_REG21_TX_DISABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REG21);
+	writel(RK3288_LVDS_CFG_REGC_PLL_DISABLE,
+	       lvds->regs + RK3288_LVDS_CFG_REGC);
+
+	clk_disable(lvds->pclk);
+
+	regulator_bulk_disable(ARRAY_SIZE(lvds->supplies),
+			       lvds->supplies);
+}
+
+static enum drm_connector_status
+rockchip_lvds_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static void rockchip_lvds_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs rockchip_lvds_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rockchip_lvds_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rockchip_lvds_connector_destroy,
+};
+
+static int rockchip_lvds_connector_get_modes(struct drm_connector *connector)
+{
+	struct rockchip_lvds *lvds = connector_to_lvds(connector);
+	struct drm_panel *panel = lvds->panel;
+
+	return panel->funcs->get_modes(panel);
+}
+
+static struct drm_encoder *
+rockchip_lvds_connector_best_encoder(struct drm_connector *connector)
+{
+	struct rockchip_lvds *lvds = connector_to_lvds(connector);
+
+	return &lvds->encoder;
+}
+
+static enum drm_mode_status rockchip_lvds_connector_mode_valid(
+		struct drm_connector *connector,
+		struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static
+struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = {
+	.get_modes = rockchip_lvds_connector_get_modes,
+	.mode_valid = rockchip_lvds_connector_mode_valid,
+	.best_encoder = rockchip_lvds_connector_best_encoder,
+};
+
+static void rockchip_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	int ret;
+
+	mutex_lock(&lvds->suspend_lock);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+		if (!lvds->suspend)
+			goto out;
+
+		drm_panel_prepare(lvds->panel);
+		ret = rockchip_lvds_poweron(lvds);
+		if (ret < 0) {
+			drm_panel_unprepare(lvds->panel);
+			return;
+		}
+		drm_panel_enable(lvds->panel);
+
+		lvds->suspend = false;
+		break;
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+	case DRM_MODE_DPMS_OFF:
+		if (lvds->suspend)
+			goto out;
+
+		drm_panel_disable(lvds->panel);
+		rockchip_lvds_poweroff(lvds);
+		drm_panel_unprepare(lvds->panel);
+
+		lvds->suspend = true;
+		break;
+	default:
+		break;
+	}
+
+out:
+	mutex_unlock(&lvds->suspend_lock);
+}
+
+static bool
+rockchip_lvds_encoder_mode_fixup(struct drm_encoder *encoder,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	u32 h_bp = mode->htotal - mode->hsync_start;
+	u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
+	u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
+	u32 val;
+	int ret;
+
+	val = lvds->format;
+	if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS)
+		val |= LVDS_DUAL | LVDS_CH0_EN | LVDS_CH1_EN;
+	else if (lvds->output == DISPLAY_OUTPUT_LVDS)
+		val |= LVDS_CH0_EN;
+	else if (lvds->output == DISPLAY_OUTPUT_RGB)
+		val |= LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN;
+
+	if (h_bp & 0x01)
+		val |= LVDS_START_PHASE_RST_1;
+
+	val |= (pin_dclk << 8) | (pin_hsync << 9);
+	val |= (0xffff << 16);
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+
+	if (lvds->output == DISPLAY_OUTPUT_RGB) {
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG0,
+					RK3288_LVDS_CH0_REG0_TTL_EN |
+					RK3288_LVDS_CH0_REG0_LANECK_EN |
+					RK3288_LVDS_CH0_REG0_LANE4_EN |
+					RK3288_LVDS_CH0_REG0_LANE3_EN |
+					RK3288_LVDS_CH0_REG0_LANE2_EN |
+					RK3288_LVDS_CH0_REG0_LANE1_EN |
+					RK3288_LVDS_CH0_REG0_LANE0_EN);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
+					RK3288_LVDS_PLL_FBDIV_REG2(0x46));
+
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG3,
+					RK3288_LVDS_PLL_FBDIV_REG3(0x46));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG4,
+					RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
+					RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG5,
+					RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
+					RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REGD,
+					RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG20,
+					RK3288_LVDS_CH0_REG20_LSB);
+	} else {
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG0,
+					RK3288_LVDS_CH0_REG0_LVDS_EN |
+					RK3288_LVDS_CH0_REG0_LANECK_EN |
+					RK3288_LVDS_CH0_REG0_LANE4_EN |
+					RK3288_LVDS_CH0_REG0_LANE3_EN |
+					RK3288_LVDS_CH0_REG0_LANE2_EN |
+					RK3288_LVDS_CH0_REG0_LANE1_EN |
+					RK3288_LVDS_CH0_REG0_LANE0_EN);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG1,
+					RK3288_LVDS_CH0_REG1_LANECK_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE4_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE3_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE2_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE1_BIAS |
+					RK3288_LVDS_CH0_REG1_LANE0_BIAS);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG2,
+					RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
+					RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
+					RK3288_LVDS_PLL_FBDIV_REG2(0x46));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG3,
+					RK3288_LVDS_PLL_FBDIV_REG3(0x46));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00);
+		lvds_writel(lvds, RK3288_LVDS_CH0_REGD,
+					RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
+		lvds_writel(lvds, RK3288_LVDS_CH0_REG20,
+					RK3288_LVDS_CH0_REG20_LSB);
+	}
+
+	dsb();
+}
+
+static void rockchip_lvds_encoder_prepare(struct drm_encoder *encoder)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	u32 val;
+	int ret;
+
+	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
+						lvds->connector.connector_type,
+						ROCKCHIP_OUT_MODE_P888);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_drm_encoder_get_mux_id(lvds->dev->of_node, encoder);
+	if (ret < 0)
+		return;
+
+	if (ret)
+		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT |
+		      (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16);
+	else
+		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
+	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
+	if (ret != 0) {
+		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+		return;
+	}
+}
+
+static void rockchip_lvds_encoder_commit(struct drm_encoder *encoder)
+{
+	rockchip_lvds_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void rockchip_lvds_encoder_disable(struct drm_encoder *encoder)
+{
+	rockchip_lvds_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static struct drm_encoder_helper_funcs rockchip_lvds_encoder_helper_funcs = {
+	.dpms = rockchip_lvds_encoder_dpms,
+	.mode_fixup = rockchip_lvds_encoder_mode_fixup,
+	.mode_set = rockchip_lvds_encoder_mode_set,
+	.prepare = rockchip_lvds_encoder_prepare,
+	.commit = rockchip_lvds_encoder_commit,
+	.disable = rockchip_lvds_encoder_disable,
+};
+
+static void rockchip_lvds_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
+	.destroy = rockchip_lvds_encoder_destroy,
+};
+
+static struct rockchip_lvds_soc_data rk3288_lvds_data = {
+	.grf_soc_con6 = 0x025c,
+	.grf_soc_con7 = 0x0260,
+};
+
+static const struct of_device_id rockchip_lvds_dt_ids[] = {
+	{
+		.compatible = "rockchip,rk3288-lvds",
+		.data = &rk3288_lvds_data
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
+
+static int rockchip_lvds_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	int ret;
+
+	lvds->drm_dev = drm_dev;
+
+	encoder = &lvds->encoder;
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_lvds_encoder_funcs,
+			       DRM_MODE_ENCODER_LVDS);
+	if (ret < 0) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs);
+
+	connector = &lvds->connector;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &rockchip_lvds_connector_funcs,
+				 DRM_MODE_CONNECTOR_LVDS);
+	if (ret < 0) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector,
+				 &rockchip_lvds_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret < 0) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	ret = drm_panel_attach(lvds->panel, connector);
+	if (ret < 0) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void rockchip_lvds_unbind(struct device *dev, struct device *master,
+				void *data)
+{
+	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+
+	if (lvds->panel) {
+		rockchip_lvds_encoder_dpms(&lvds->encoder, DRM_MODE_DPMS_OFF);
+
+		drm_panel_detach(lvds->panel);
+
+		drm_connector_cleanup(&lvds->connector);
+		drm_encoder_cleanup(&lvds->encoder);
+	}
+}
+static const struct component_ops rockchip_lvds_component_ops = {
+	.bind = rockchip_lvds_bind,
+	.unbind = rockchip_lvds_unbind,
+};
+
+static int rockchip_lvds_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_lvds *lvds;
+	struct device_node *port, *output_node = NULL;
+	const struct of_device_id *match;
+	struct resource *res;
+	const char *name;
+	int i, ret;
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
+	if (!lvds)
+		return -ENOMEM;
+
+	lvds->dev = dev;
+	lvds->suspend = true;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	lvds->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(lvds->regs))
+		return PTR_ERR(lvds->regs);
+
+	lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+						    "rockchip,grf");
+	if (IS_ERR(lvds->grf)) {
+		dev_err(dev, "missing rockchip,grf property\n");
+		return PTR_ERR(lvds->grf);
+	}
+
+	lvds->supplies[0].supply = "avdd1v0";
+	lvds->supplies[1].supply = "avdd1v8";
+	lvds->supplies[2].supply = "avdd3v3";
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(lvds->supplies),
+				      lvds->supplies);
+	if (ret < 0)
+		return ret;
+
+	lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds");
+	if (IS_ERR(lvds->pclk)) {
+		dev_err(dev, "could not get pclk_lvds\n");
+		return PTR_ERR(lvds->pclk);
+	}
+
+	ret = clk_prepare(lvds->pclk);
+	if (ret < 0) {
+		dev_err(dev, "failed to prepare pclk_lvds\n");
+		return ret;
+	}
+
+	match = of_match_node(rockchip_lvds_dt_ids, dev->of_node);
+	lvds->soc_data = match->data;
+
+	dev_set_drvdata(dev, lvds);
+	mutex_init(&lvds->suspend_lock);
+
+	if (of_property_read_string(dev->of_node, "rockchip,output", &name))
+		/* default set it as output rgb */
+		lvds->output = DISPLAY_OUTPUT_RGB;
+	else
+		lvds->output = lvds_name_to_output(name);
+
+	if (of_property_read_string(dev->of_node, "rockchip,data-mapping",
+				    &name))
+		/* default set it as format jeida */
+		lvds->format = LVDS_FORMAT_JEIDA;
+	else
+		lvds->format = lvds_name_to_format(name);
+
+	if (of_property_read_u32(dev->of_node, "rockchip,data-width", &i)) {
+		lvds->format |= LVDS_24BIT;
+	} else {
+		if (i == 24) {
+			lvds->format |= LVDS_24BIT;
+		} else if (i == 18) {
+			lvds->format |= LVDS_18BIT;
+		} else {
+			dev_err(&pdev->dev,
+				"rockchip-lvds unsupport data-width[%d]\n", i);
+			ret = -EINVAL;
+			goto err_unprepare_pclk;
+		}
+	}
+
+	port = of_graph_get_port_by_id(dev->of_node, 1);
+	if (port) {
+		struct device_node *endpoint;
+
+		endpoint = of_get_child_by_name(port, "endpoint");
+		if (endpoint) {
+			output_node = of_graph_get_remote_port_parent(endpoint);
+			of_node_put(endpoint);
+		}
+	}
+
+	if (!output_node) {
+		dev_err(&pdev->dev, "no output defined\n");
+		return -EINVAL;
+	}
+
+	lvds->panel = of_drm_find_panel(output_node);
+	of_node_put(output_node);
+	if (!lvds->panel) {
+		dev_err(&pdev->dev, "panel not found\n");
+		return -EPROBE_DEFER;
+	}
+
+	return component_add(&pdev->dev, &rockchip_lvds_component_ops);
+
+err_unprepare_pclk:
+	clk_unprepare(lvds->pclk);
+	return ret;
+}
+
+static int rockchip_lvds_remove(struct platform_device *pdev)
+{
+	struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev);
+
+	component_del(&pdev->dev, &rockchip_lvds_component_ops);
+
+	clk_unprepare(lvds->pclk);
+
+	return 0;
+}
+
+struct platform_driver rockchip_lvds_driver = {
+	.probe = rockchip_lvds_probe,
+	.remove = rockchip_lvds_remove,
+	.driver = {
+		   .name = "rockchip-lvds",
+		   .of_match_table = of_match_ptr(rockchip_lvds_dt_ids),
+	},
+};
+module_platform_driver(rockchip_lvds_driver);
+
+MODULE_AUTHOR("Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>");
+MODULE_AUTHOR("Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>");
+MODULE_DESCRIPTION("ROCKCHIP LVDS Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.h b/drivers/gpu/drm/rockchip/rockchip_lvds.h
new file mode 100644
index 0000000..9b59d87
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      hjc <hjc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *      mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+ *
+ * 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_LVDS_
+#define _ROCKCHIP_LVDS_
+
+#define RK3288_LVDS_CH0_REG0			0x00
+#define RK3288_LVDS_CH0_REG0_LVDS_EN		BIT(7)
+#define RK3288_LVDS_CH0_REG0_TTL_EN		BIT(6)
+#define RK3288_LVDS_CH0_REG0_LANECK_EN		BIT(5)
+#define RK3288_LVDS_CH0_REG0_LANE4_EN		BIT(4)
+#define RK3288_LVDS_CH0_REG0_LANE3_EN		BIT(3)
+#define RK3288_LVDS_CH0_REG0_LANE2_EN		BIT(2)
+#define RK3288_LVDS_CH0_REG0_LANE1_EN		BIT(1)
+#define RK3288_LVDS_CH0_REG0_LANE0_EN		BIT(0)
+
+#define RK3288_LVDS_CH0_REG1			0x04
+#define RK3288_LVDS_CH0_REG1_LANECK_BIAS	BIT(5)
+#define RK3288_LVDS_CH0_REG1_LANE4_BIAS		BIT(4)
+#define RK3288_LVDS_CH0_REG1_LANE3_BIAS		BIT(3)
+#define RK3288_LVDS_CH0_REG1_LANE2_BIAS		BIT(2)
+#define RK3288_LVDS_CH0_REG1_LANE1_BIAS		BIT(1)
+#define RK3288_LVDS_CH0_REG1_LANE0_BIAS		BIT(0)
+
+#define RK3288_LVDS_CH0_REG2			0x08
+#define RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE	BIT(6)
+#define RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE	BIT(5)
+#define RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE	BIT(4)
+#define RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE	BIT(3)
+#define RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE	BIT(2)
+#define RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE	BIT(1)
+#define RK3288_LVDS_CH0_REG2_PLL_FBDIV8		BIT(0)
+
+#define RK3288_LVDS_CH0_REG3			0x0c
+#define RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK	0xff
+
+#define RK3288_LVDS_CH0_REG4			0x10
+#define RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE	BIT(5)
+#define RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE	BIT(4)
+#define RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE	BIT(3)
+#define RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE	BIT(2)
+#define RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE	BIT(1)
+#define RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE	BIT(0)
+
+#define RK3288_LVDS_CH0_REG5			0x14
+#define RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA	BIT(5)
+#define RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA	BIT(4)
+#define RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA	BIT(3)
+#define RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA	BIT(2)
+#define RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA	BIT(1)
+#define RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA	BIT(0)
+
+#define RK3288_LVDS_CFG_REGC			0x30
+#define RK3288_LVDS_CFG_REGC_PLL_ENABLE		0x00
+#define RK3288_LVDS_CFG_REGC_PLL_DISABLE	0xff
+
+#define RK3288_LVDS_CH0_REGD			0x34
+#define RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK	0x1f
+
+#define RK3288_LVDS_CH0_REG20			0x80
+#define RK3288_LVDS_CH0_REG20_MSB		0x45
+#define RK3288_LVDS_CH0_REG20_LSB		0x44
+
+#define RK3288_LVDS_CFG_REG21			0x84
+#define RK3288_LVDS_CFG_REG21_TX_ENABLE		0x92
+#define RK3288_LVDS_CFG_REG21_TX_DISABLE	0x00
+
+/* fbdiv value is split over 2 registers, with bit8 in reg2 */
+#define RK3288_LVDS_PLL_FBDIV_REG2(_fbd) \
+		(_fbd & BIT(8) ? RK3288_LVDS_CH0_REG2_PLL_FBDIV8 : 0)
+#define RK3288_LVDS_PLL_FBDIV_REG3(_fbd) \
+		(_fbd & RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK)
+#define RK3288_LVDS_PLL_PREDIV_REGD(_pd) \
+		(_pd & RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK)
+
+#define RK3288_LVDS_SOC_CON6_SEL_VOP_LIT	BIT(3)
+
+#define LVDS_FMT_MASK			(0x07 << 16)
+#define LVDS_MSB			(0x01 << 3)
+#define LVDS_DUAL			(0x01 << 4)
+#define LVDS_FMT_1			(0x01 << 5)
+#define LVDS_TTL_EN			(0x01 << 6)
+#define LVDS_START_PHASE_RST_1		(0x01 << 7)
+#define LVDS_DCLK_INV			(0x01 << 8)
+#define LVDS_CH0_EN			(0x01 << 11)
+#define LVDS_CH1_EN			(0x01 << 12)
+#define LVDS_PWRDN			(0x01 << 15)
+
+#define LVDS_24BIT		(0 << 1)
+#define LVDS_18BIT		(1 << 1)
+#define LVDS_FORMAT_VESA	(0 << 0)
+#define LVDS_FORMAT_JEIDA	(1 << 0)
+
+#endif /* _ROCKCHIP_LVDS_ */
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 08/12] drm/rockchip: lvds: register a bridge when no panel is set
       [not found] ` <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
                     ` (3 preceding siblings ...)
  2015-04-01 10:09   ` [PATCH RFC v2 07/12] drm/rockchip: Add support for Rockchip Soc LVDS Heiko Stuebner
@ 2015-04-01 10:09   ` Heiko Stuebner
  4 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, mark.yao-TNX95d0MmH7DzftRWevZcw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw
  Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w, djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Heiko Stuebner

On socs using the lvds components it also controls the use of the
general rgb outputs and must thus be configured for things like
external encoders.

Therefore register a drm_bridge in this case and try to find
the encoder in the output port.

Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
 drivers/gpu/drm/rockchip/rockchip_lvds.c | 255 ++++++++++++++++++++++++++++---
 1 file changed, 233 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index 657609e..5ffd70a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -43,6 +43,9 @@
 #define encoder_to_lvds(c) \
 		container_of(c, struct rockchip_lvds, encoder)
 
+#define bridge_to_lvds(c) \
+		container_of(c, struct rockchip_lvds, bridge)
+
 /*
  * @grf_offset: offset inside the grf regmap for setting the rockchip lvds
  */
@@ -68,6 +71,8 @@ struct rockchip_lvds {
 	struct drm_panel *panel;
 	struct drm_connector connector;
 	struct drm_encoder encoder;
+	struct drm_bridge bridge;
+	struct drm_encoder *ext_encoder;
 
 	struct mutex suspend_lock;
 	int suspend;
@@ -248,11 +253,10 @@ rockchip_lvds_encoder_mode_fixup(struct drm_encoder *encoder,
 	return true;
 }
 
-static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
-					  struct drm_display_mode *mode,
-					  struct drm_display_mode *adjusted)
+static void rockchip_lvds_mode_set(struct rockchip_lvds *lvds,
+				   struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted)
 {
-	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
 	u32 h_bp = mode->htotal - mode->hsync_start;
 	u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
 	u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
@@ -347,32 +351,52 @@ static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
 	dsb();
 }
 
-static void rockchip_lvds_encoder_prepare(struct drm_encoder *encoder)
+static void rockchip_lvds_encoder_mode_set(struct drm_encoder *encoder,
+					   struct drm_display_mode *mode,
+					   struct drm_display_mode *adjusted)
+{
+	rockchip_lvds_mode_set(encoder_to_lvds(encoder), mode, adjusted);
+}
+
+static int rockchip_lvds_set_vop_source(struct rockchip_lvds *lvds,
+					struct drm_encoder *encoder)
 {
-	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
 	u32 val;
 	int ret;
 
-	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
-						lvds->connector.connector_type,
-						ROCKCHIP_OUT_MODE_P888);
-	if (ret < 0) {
-		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
-		return;
-	}
-
 	ret = rockchip_drm_encoder_get_mux_id(lvds->dev->of_node, encoder);
 	if (ret < 0)
-		return;
+		return ret;
 
 	if (ret)
 		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT |
 		      (RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16);
 	else
 		val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
+
 	ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val);
-	if (ret != 0) {
-		dev_err(lvds->dev, "Could not write to GRF: %d\n", ret);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void rockchip_lvds_encoder_prepare(struct drm_encoder *encoder)
+{
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	int ret;
+
+	ret = rockchip_drm_crtc_mode_config(encoder->crtc,
+						lvds->connector.connector_type,
+						ROCKCHIP_OUT_MODE_P888);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_lvds_set_vop_source(lvds, encoder);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set vop source: %d\n", ret);
 		return;
 	}
 }
@@ -405,6 +429,97 @@ static struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
 	.destroy = rockchip_lvds_encoder_destroy,
 };
 
+static void rockchip_lvds_bridge_mode_set(struct drm_bridge *bridge,
+					  struct drm_display_mode *mode,
+					  struct drm_display_mode *adjusted)
+{
+	rockchip_lvds_mode_set(bridge_to_lvds(bridge), mode, adjusted);
+}
+
+static void rockchip_lvds_bridge_pre_enable(struct drm_bridge *bridge)
+{
+}
+
+/*
+ * post_disable is called right after encoder prepare, so do lvds and crtc
+ * mode config here.
+ */
+static void rockchip_lvds_bridge_post_disable(struct drm_bridge *bridge)
+{
+	struct rockchip_lvds *lvds = bridge_to_lvds(bridge);
+	struct drm_connector *connector;
+	int ret, connector_type = DRM_MODE_CONNECTOR_Unknown;
+
+	if (!bridge->encoder->crtc)
+		return;
+
+	list_for_each_entry(connector, &bridge->dev->mode_config.connector_list,
+			head) {
+		if (connector->encoder == bridge->encoder)
+			connector_type = connector->connector_type;
+	}
+
+	ret = rockchip_drm_crtc_mode_config(bridge->encoder->crtc,
+						connector_type,
+						ROCKCHIP_OUT_MODE_P888);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set crtc mode config: %d\n", ret);
+		return;
+	}
+
+	ret = rockchip_lvds_set_vop_source(lvds, bridge->encoder);
+	if (ret < 0) {
+		dev_err(lvds->dev, "Could not set vop source: %d\n", ret);
+		return;
+	}
+}
+
+static void rockchip_lvds_bridge_enable(struct drm_bridge *bridge)
+{
+	struct rockchip_lvds *lvds = bridge_to_lvds(bridge);
+	int ret;
+
+	mutex_lock(&lvds->suspend_lock);
+
+	if (!lvds->suspend)
+		goto out;
+
+	ret = rockchip_lvds_poweron(lvds);
+	if (ret < 0) {
+		dev_err(lvds->dev, "could not enable lvds\n");
+		goto out;
+	}
+
+	lvds->suspend = false;
+
+out:
+	mutex_unlock(&lvds->suspend_lock);
+}
+
+static void rockchip_lvds_bridge_disable(struct drm_bridge *bridge)
+{
+	struct rockchip_lvds *lvds = bridge_to_lvds(bridge);
+
+	mutex_lock(&lvds->suspend_lock);
+
+	if (lvds->suspend)
+		goto out;
+
+	rockchip_lvds_poweroff(lvds);
+	lvds->suspend = true;
+
+out:
+	mutex_unlock(&lvds->suspend_lock);
+}
+
+static struct drm_bridge_funcs rockchip_lvds_bridge_funcs = {
+	.mode_set = rockchip_lvds_bridge_mode_set,
+	.enable = rockchip_lvds_bridge_enable,
+	.disable = rockchip_lvds_bridge_disable,
+	.pre_enable = rockchip_lvds_bridge_pre_enable,
+	.post_disable = rockchip_lvds_bridge_post_disable,
+};
+
 static struct rockchip_lvds_soc_data rk3288_lvds_data = {
 	.grf_soc_con6 = 0x025c,
 	.grf_soc_con7 = 0x0260,
@@ -430,6 +545,35 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 
 	lvds->drm_dev = drm_dev;
 
+	if (!lvds->panel) {
+		struct drm_bridge *bridge = &lvds->bridge;
+
+		if (!lvds->ext_encoder->of_node)
+			return -ENODEV;
+
+		ret = component_bind_all(dev, drm_dev);
+		if (ret < 0)
+			return ret;
+
+		/**
+		 * Override any possible crtcs set by the encoder itself,
+		 * as they are connected to the lvds instead.
+		 */
+		encoder = of_drm_find_encoder(lvds->ext_encoder->of_node);
+		encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+								dev->of_node);
+
+		encoder->bridge = bridge;
+		bridge->encoder = encoder;
+		ret = drm_bridge_attach(drm_dev, bridge);
+		if (ret < 0) {
+			component_unbind_all(dev, drm_dev);
+			return ret;
+		}
+
+		return 0;
+	}
+
 	encoder = &lvds->encoder;
 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
 							     dev->of_node);
@@ -482,6 +626,7 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 				void *data)
 {
 	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
 
 	if (lvds->panel) {
 		rockchip_lvds_encoder_dpms(&lvds->encoder, DRM_MODE_DPMS_OFF);
@@ -490,6 +635,8 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 
 		drm_connector_cleanup(&lvds->connector);
 		drm_encoder_cleanup(&lvds->encoder);
+	} else {
+		component_unbind_all(dev, drm_dev);
 	}
 }
 static const struct component_ops rockchip_lvds_component_ops = {
@@ -497,6 +644,26 @@ static const struct component_ops rockchip_lvds_component_ops = {
 	.unbind = rockchip_lvds_unbind,
 };
 
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int rockchip_lvds_master_bind(struct device *dev)
+{
+	return 0;
+}
+
+static void rockchip_lvds_master_unbind(struct device *dev)
+{
+	/* do nothing */
+}
+
+static const struct component_master_ops rockchip_lvds_master_ops = {
+	.bind = rockchip_lvds_master_bind,
+	.unbind = rockchip_lvds_master_unbind,
+};
+
 static int rockchip_lvds_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -596,18 +763,58 @@ static int rockchip_lvds_probe(struct platform_device *pdev)
 
 	if (!output_node) {
 		dev_err(&pdev->dev, "no output defined\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err_unprepare_pclk;
 	}
 
 	lvds->panel = of_drm_find_panel(output_node);
-	of_node_put(output_node);
 	if (!lvds->panel) {
-		dev_err(&pdev->dev, "panel not found\n");
-		return -EPROBE_DEFER;
+		struct drm_encoder *encoder;
+		struct component_match *match = NULL;
+
+		/* Try to find an encoder in the output node */
+		encoder = of_drm_find_encoder(output_node);
+		if (!encoder) {
+			dev_err(&pdev->dev, "neither panel nor encoder found\n");
+			of_node_put(output_node);
+			ret = -EPROBE_DEFER;
+			goto err_unprepare_pclk;
+		}
+
+		lvds->ext_encoder = encoder;
+
+		component_match_add(dev, &match, compare_of, output_node);
+		of_node_put(output_node);
+
+		lvds->bridge.funcs = &rockchip_lvds_bridge_funcs;
+		lvds->bridge.of_node = dev->of_node;
+		ret = drm_bridge_add(&lvds->bridge);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to add bridge %d\n", ret);
+			goto err_unprepare_pclk;
+		}
+
+		ret = component_master_add_with_match(dev,
+						      &rockchip_lvds_master_ops,
+						      match);
+		if (ret < 0)
+			goto err_bridge_remove;
+	} else {
+		of_node_put(output_node);
 	}
 
-	return component_add(&pdev->dev, &rockchip_lvds_component_ops);
+	ret = component_add(&pdev->dev, &rockchip_lvds_component_ops);
+	if (ret < 0)
+		goto err_master_remove;
+
+	return 0;
 
+err_master_remove:
+	if (!lvds->panel)
+		component_master_del(&pdev->dev, &rockchip_lvds_master_ops);
+err_bridge_remove:
+	if (!lvds->panel)
+		drm_bridge_remove(&lvds->bridge);
 err_unprepare_pclk:
 	clk_unprepare(lvds->pclk);
 	return ret;
@@ -618,6 +825,10 @@ static int rockchip_lvds_remove(struct platform_device *pdev)
 	struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev);
 
 	component_del(&pdev->dev, &rockchip_lvds_component_ops);
+	if (!lvds->panel) {
+		component_master_del(&pdev->dev, &rockchip_lvds_master_ops);
+		drm_bridge_remove(&lvds->bridge);
+	}
 
 	clk_unprepare(lvds->pclk);
 
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 09/12] drm/rockchip: enable rgb output of vops for all other connectors
  2015-04-01 10:09 [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
                   ` (3 preceding siblings ...)
  2015-04-01 10:09 ` [PATCH RFC v2 06/12] dt-bindings: Add documentation for rockchip lvds Heiko Stuebner
@ 2015-04-01 10:09 ` Heiko Stuebner
  2015-04-01 10:09 ` [PATCH RFC v2 10/12] ARM: dts: rockchip: add rk3288 lcdc0 pinmux settings Heiko Stuebner
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied, mark.yao, laurent.pinchart
  Cc: devicetree, dri-devel, linux-rockchip, linux-arm-kernel

The socs itself contains encoders for a lot of different outputs. But every
unsupported connector will be routed through the lvds, as it controls the
pins in question.
Therefore enable the lvds output for all of those.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c0387f7..3844b12 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -862,20 +862,16 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
 	clk_disable(vop->dclk);
 
 	switch (vop->connector_type) {
-	case DRM_MODE_CONNECTOR_LVDS:
-		VOP_CTRL_SET(vop, rgb_en, 1);
-		break;
 	case DRM_MODE_CONNECTOR_eDP:
 		VOP_CTRL_SET(vop, edp_en, 1);
 		break;
 	case DRM_MODE_CONNECTOR_HDMIA:
 		VOP_CTRL_SET(vop, hdmi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_LVDS:
 	default:
-		DRM_ERROR("unsupport connector_type[%d]\n",
-			  vop->connector_type);
-		ret = -EINVAL;
-		goto out;
+		VOP_CTRL_SET(vop, rgb_en, 1);
+		break;
 	};
 	VOP_CTRL_SET(vop, out_mode, vop->connector_out_mode);
 
-- 
2.1.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 10/12] ARM: dts: rockchip: add rk3288 lcdc0 pinmux settings
  2015-04-01 10:09 [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
                   ` (4 preceding siblings ...)
  2015-04-01 10:09 ` [PATCH RFC v2 09/12] drm/rockchip: enable rgb output of vops for all other connectors Heiko Stuebner
@ 2015-04-01 10:09 ` Heiko Stuebner
  2015-04-01 10:09 ` [PATCH RFC v2 11/12] ARM: dts: rockchip: add rk3288 lvds node Heiko Stuebner
  2015-04-01 10:09 ` [PATCH RFC v2 12/12] ARM: dts: rockchip: add vga encoder and enable lvds on rk3288-firefly Heiko Stuebner
  7 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied, mark.yao, laurent.pinchart
  Cc: devicetree, dri-devel, linux-rockchip, linux-arm-kernel

Add pinctrl settings for the configurable lcdc0 signals dclk, den, hsync
and vsync. The lcdc0 data pin configuration is not software controlable.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 arch/arm/boot/dts/rk3288.dtsi | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 2c29a3e..e7e94f8 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -945,6 +945,15 @@
 			};
 		};
 
+		lcdc0 {
+			lcdc0_ctl: lcdc0-ctl {
+				rockchip,pins = <1 24 RK_FUNC_1 &pcfg_pull_none>,
+						<1 25 RK_FUNC_1 &pcfg_pull_none>,
+						<1 26 RK_FUNC_1 &pcfg_pull_none>,
+						<1 27 RK_FUNC_1 &pcfg_pull_none>;
+			};
+		};
+
 		sdmmc {
 			sdmmc_clk: sdmmc-clk {
 				rockchip,pins = <6 20 RK_FUNC_1 &pcfg_pull_none>;
-- 
2.1.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 11/12] ARM: dts: rockchip: add rk3288 lvds node
  2015-04-01 10:09 [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
                   ` (5 preceding siblings ...)
  2015-04-01 10:09 ` [PATCH RFC v2 10/12] ARM: dts: rockchip: add rk3288 lcdc0 pinmux settings Heiko Stuebner
@ 2015-04-01 10:09 ` Heiko Stuebner
  2015-04-01 10:09 ` [PATCH RFC v2 12/12] ARM: dts: rockchip: add vga encoder and enable lvds on rk3288-firefly Heiko Stuebner
  7 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied, mark.yao, laurent.pinchart
  Cc: devicetree, dri-devel, linux-rockchip, linux-arm-kernel

Add the basic node for the lvds controller of rk3288 and hook it into the
display-subsystem hirarchy.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 arch/arm/boot/dts/rk3288.dtsi | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index e7e94f8..97291bb 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -621,6 +621,11 @@
 				reg = <0>;
 				remote-endpoint = <&hdmi_in_vopb>;
 			};
+
+			vopb_out_lvds: endpoint@1 {
+				reg = <1>;
+				remote-endpoint = <&lvds_in_vopb>;
+			};
 		};
 	};
 
@@ -652,6 +657,11 @@
 				reg = <0>;
 				remote-endpoint = <&hdmi_in_vopl>;
 			};
+
+			vopl_out_lvds: endpoint@1 {
+				reg = <1>;
+				remote-endpoint = <&lvds_in_vopl>;
+			};
 		};
 	};
 
@@ -664,6 +674,38 @@
 		status = "disabled";
 	};
 
+	lvds: lvds@ff96c000 {
+		compatible = "rockchip,rk3288-lvds";
+		reg = <0xff96c000 0x4000>;
+		clocks = <&cru PCLK_LVDS_PHY>;
+		clock-names = "pclk_lvds";
+		pinctrl-names = "default";
+		pinctrl-0 = <&lcdc0_ctl>;
+		rockchip,grf = <&grf>;
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			lvds_in: port@0 {
+				reg = <0>;
+
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				lvds_in_vopb: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_lvds>;
+				};
+				lvds_in_vopl: endpoint@1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_lvds>;
+				};
+			};
+		};
+	};
+
 	hdmi: hdmi@ff980000 {
 		compatible = "rockchip,rk3288-dw-hdmi";
 		reg = <0xff980000 0x20000>;
-- 
2.1.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH RFC v2 12/12] ARM: dts: rockchip: add vga encoder and enable lvds on rk3288-firefly
  2015-04-01 10:09 [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
                   ` (6 preceding siblings ...)
  2015-04-01 10:09 ` [PATCH RFC v2 11/12] ARM: dts: rockchip: add rk3288 lvds node Heiko Stuebner
@ 2015-04-01 10:09 ` Heiko Stuebner
  7 siblings, 0 replies; 15+ messages in thread
From: Heiko Stuebner @ 2015-04-01 10:09 UTC (permalink / raw)
  To: airlied, mark.yao, laurent.pinchart
  Cc: devicetree, dri-devel, linux-rockchip, linux-arm-kernel

Add the sda7123 simple vga encoder, connect it to the vop outputs
and enable the lvds controller with the correct settings.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 arch/arm/boot/dts/rk3288-firefly.dtsi | 68 +++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index b54dd78..2cce2a9 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -159,6 +159,51 @@
 		regulator-always-on;
 		vin-supply = <&vcc_5v>;
 	};
+
+	sda7123: vga-encoder {
+		compatible = "adi,adv7123";
+		psave-gpios = <&gpio0 17 GPIO_ACTIVE_HIGH>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				sda7123_in: endpoint@0 {
+					remote-endpoint = <&lvds_out>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				sda7123_out: endpoint@0 {
+					remote-endpoint = <&vga_connector_in>;
+				};
+			};
+		};
+	};
+
+	vga: vga-connector {
+		compatible = "vga-connector";
+		label = "vga";
+
+		ddc-i2c-bus = <&i2c4>;
+
+		port {
+			vga_connector_in: endpoint {
+				remote-endpoint = <&sda7123_out>;
+			};
+		};
+	};
 };
 
 &cpu0 {
@@ -347,6 +392,29 @@
 	status = "okay";
 };
 
+&lvds {
+	avdd1v0-supply = <&vdd10_lcd>;
+	avdd1v8-supply = <&vcc18_lcd>;
+	avdd3v3-supply = <&vcca_33>;
+	rockchip,data-mapping = "jeida";
+	rockchip,data-width = <24>;
+	rockchip,output = "rgb";
+	status = "okay";
+
+	ports {
+		port@1 {
+			reg = <1>;
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			lvds_out: endpoint@0 {
+				remote-endpoint = <&sda7123_in>;
+			};
+		};
+	};
+};
+
 &pinctrl {
 	pcfg_output_high: pcfg-output-high {
 		output-high;
-- 
2.1.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH RFC v2 04/12] drm/components: add generic vga encoder driver
       [not found]   ` <1427882986-19110-5-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
@ 2015-04-01 10:27     ` Russell King - ARM Linux
  2015-04-01 10:40       ` Heiko Stübner
  0 siblings, 1 reply; 15+ messages in thread
From: Russell King - ARM Linux @ 2015-04-01 10:27 UTC (permalink / raw)
  To: Heiko Stuebner
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	airlied-cv59FeDIM0c, dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	robdclark-Re5JQEeQqe8AvxtiuMwx3w,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	mark.yao-TNX95d0MmH7DzftRWevZcw

On Wed, Apr 01, 2015 at 12:09:38PM +0200, Heiko Stuebner wrote:
> This adds a driver for generic vga encoders like the Analog Devices adv7123
> and similar ics. These chips do not have any special configuration options
> except a powersafe gpio.
> 
> An exception is added for the rcar-du driver which also implements support
> for the adv7123 internally but is not yet converted to the component framework.

Why is this implemented as a master?  It's not the top level card device,
it shouldn't be a master.  It should be a component.

I wonder if this is why you need your previous patches.  Componentised
DRM works fine without needing to build lists of connectors and encoders.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH RFC v2 04/12] drm/components: add generic vga encoder driver
  2015-04-01 10:27     ` Russell King - ARM Linux
@ 2015-04-01 10:40       ` Heiko Stübner
  0 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2015-04-01 10:40 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: devicetree, dri-devel, linux-rockchip, laurent.pinchart,
	linux-arm-kernel

Hi Russell,

Am Mittwoch, 1. April 2015, 11:27:51 schrieb Russell King - ARM Linux:
> On Wed, Apr 01, 2015 at 12:09:38PM +0200, Heiko Stuebner wrote:
> > This adds a driver for generic vga encoders like the Analog Devices
> > adv7123
> > and similar ics. These chips do not have any special configuration options
> > except a powersafe gpio.
> > 
> > An exception is added for the rcar-du driver which also implements support
> > for the adv7123 internally but is not yet converted to the component
> > framework.
> Why is this implemented as a master?  It's not the top level card device,
> it shouldn't be a master.  It should be a component.
> 
> I wonder if this is why you need your previous patches.  Componentised
> DRM works fine without needing to build lists of connectors and encoders.

It is a component to the upper drm driver (rockchip or whatever) but a master 
to the vga connector below, simply because the drm driver above (the lvds 
controller in this case) does not know and also probably shouldn't care what 
the structure below is.

So in this case the lvds knows there is either a panel or an encoder 
connected, but does not need to handle if this downstream component is some 
one-piece i2c component like a tv encoder or contains another component down 
below, like the vga-connector in this case.


Of course I may very well have overlooked some simpler method to achieve this, 
but got inspired to this by the similar way done in sti/sti_tvout.c


Heiko

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2015-04-01 10:40 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-04-01 10:09 [PATCH RFC v2 00/12] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 01/12] drm/encoder: add functionality to register encoders to a global list Heiko Stuebner
     [not found] ` <1427882986-19110-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
2015-04-01 10:09   ` [PATCH RFC v2 02/12] drm/connector: add functionality to register connectors " Heiko Stuebner
2015-04-01 10:09   ` [PATCH RFC v2 03/12] drm: add components subdirectory and infrastructure Heiko Stuebner
2015-04-01 10:09   ` [PATCH RFC v2 05/12] drm/components: add generic vga connector driver Heiko Stuebner
2015-04-01 10:09   ` [PATCH RFC v2 07/12] drm/rockchip: Add support for Rockchip Soc LVDS Heiko Stuebner
2015-04-01 10:09   ` [PATCH RFC v2 08/12] drm/rockchip: lvds: register a bridge when no panel is set Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 04/12] drm/components: add generic vga encoder driver Heiko Stuebner
     [not found]   ` <1427882986-19110-5-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
2015-04-01 10:27     ` Russell King - ARM Linux
2015-04-01 10:40       ` Heiko Stübner
2015-04-01 10:09 ` [PATCH RFC v2 06/12] dt-bindings: Add documentation for rockchip lvds Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 09/12] drm/rockchip: enable rgb output of vops for all other connectors Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 10/12] ARM: dts: rockchip: add rk3288 lcdc0 pinmux settings Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 11/12] ARM: dts: rockchip: add rk3288 lvds node Heiko Stuebner
2015-04-01 10:09 ` [PATCH RFC v2 12/12] ARM: dts: rockchip: add vga encoder and enable lvds on rk3288-firefly Heiko Stuebner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).