All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/11] drm/rockchip: add support for lvds controller and external encoders
@ 2015-01-31 16:32 ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: airlied, mark.yao
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, dri-devel,
	linux-rockchip, robh+dt, galak, linux-arm-kernel

This series adds support for the lvds encoder present on rk3288 soc and
allows external connectors to use the generic rgb pins.

On the older socs (rk3188, rk3066, etc) these pins where accessible by
anyone, while on the rk3288 the lvds controller controls access to them.

So while on the old socs an external encoder was explicitly connected
to one of the two lcd-controllers, on the rk3288 the lvds in between
can toggle which controller should be the source.

To facilitate this the lvds encoder can use two modes. When a panel is
attached it acts as encoder and without panel it just registers a bridge
that can be used later.

The bridge association to an encoder is done via a rockchip,rgb-bridge
property in the encoder node itself and handled in rockchip_drm_load
to not leak rockchip-specific handling into generic encoder drivers.


As example on how this can work, I've included a driver for simple
(dumb) vga encoders, like the adv7123 (and clones) as used on the
rk3288-firefly board. This same encoder is used on the Rayeager-px2
board but there connected directly to the rgb pins of the rk3066
which will hopefully also be supported in the future.

I've named this currently vga-simply (inspired by panel-simple), because
so far I have found the adv7123 (and two clones) but I guess there will
be more dumb vga encoders around that only differ in minimal things.


Similarly, most of the rk3288-based TV-boxes use a rk1000 i2c tv encoder
connected in a similar way - and again directly connected on the
rk3188-radxarock.


Caveats:
- the i2c subdirectory is probably not the right one for my vga encoder
  so if somebody could suggest where this should live, I'd be very happy
- I'm not sure if I'm abusing some drm-APIs in a wrong way :-)


Heiko Stuebner (9):
  drm/encoder: allow encoders to remember their of_node
  drm: add bindings for simple vga encoders
  drm: add driver for simple vga encoders
  drm/rockchip: lvds: register a bridge when no panel is set
  drm/rockchip: attach rgb bridge to encoders needing it
  drm/rockchip: enable rgb ouput of vops for vga and tv 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/drm/i2c/vga-simple.txt     |  18 +
 .../devicetree/bindings/video/rockchip-lvds.txt    |  63 ++
 .../devicetree/bindings/video/rockchip-vop.txt     |  16 +
 arch/arm/boot/dts/rk3288-firefly.dtsi              |  45 ++
 arch/arm/boot/dts/rk3288.dtsi                      |  45 ++
 drivers/gpu/drm/i2c/Kconfig                        |   6 +
 drivers/gpu/drm/i2c/Makefile                       |   2 +
 drivers/gpu/drm/i2c/vga-simple.c                   | 325 +++++++++
 drivers/gpu/drm/rockchip/Kconfig                   |   9 +
 drivers/gpu/drm/rockchip/Makefile                  |   1 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c        |  32 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |   2 +
 drivers/gpu/drm/rockchip/rockchip_lvds.c           | 756 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.h           | 107 +++
 include/drm/drm_crtc.h                             |   4 +
 15 files changed, 1431 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-lvds.txt
 create mode 100644 drivers/gpu/drm/i2c/vga-simple.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.h

-- 
2.1.1

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

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

* [PATCH 00/11] drm/rockchip: add support for lvds controller and external encoders
@ 2015-01-31 16:32 ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

This series adds support for the lvds encoder present on rk3288 soc and
allows external connectors to use the generic rgb pins.

On the older socs (rk3188, rk3066, etc) these pins where accessible by
anyone, while on the rk3288 the lvds controller controls access to them.

So while on the old socs an external encoder was explicitly connected
to one of the two lcd-controllers, on the rk3288 the lvds in between
can toggle which controller should be the source.

To facilitate this the lvds encoder can use two modes. When a panel is
attached it acts as encoder and without panel it just registers a bridge
that can be used later.

The bridge association to an encoder is done via a rockchip,rgb-bridge
property in the encoder node itself and handled in rockchip_drm_load
to not leak rockchip-specific handling into generic encoder drivers.


As example on how this can work, I've included a driver for simple
(dumb) vga encoders, like the adv7123 (and clones) as used on the
rk3288-firefly board. This same encoder is used on the Rayeager-px2
board but there connected directly to the rgb pins of the rk3066
which will hopefully also be supported in the future.

I've named this currently vga-simply (inspired by panel-simple), because
so far I have found the adv7123 (and two clones) but I guess there will
be more dumb vga encoders around that only differ in minimal things.


Similarly, most of the rk3288-based TV-boxes use a rk1000 i2c tv encoder
connected in a similar way - and again directly connected on the
rk3188-radxarock.


Caveats:
- the i2c subdirectory is probably not the right one for my vga encoder
  so if somebody could suggest where this should live, I'd be very happy
- I'm not sure if I'm abusing some drm-APIs in a wrong way :-)


Heiko Stuebner (9):
  drm/encoder: allow encoders to remember their of_node
  drm: add bindings for simple vga encoders
  drm: add driver for simple vga encoders
  drm/rockchip: lvds: register a bridge when no panel is set
  drm/rockchip: attach rgb bridge to encoders needing it
  drm/rockchip: enable rgb ouput of vops for vga and tv 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/drm/i2c/vga-simple.txt     |  18 +
 .../devicetree/bindings/video/rockchip-lvds.txt    |  63 ++
 .../devicetree/bindings/video/rockchip-vop.txt     |  16 +
 arch/arm/boot/dts/rk3288-firefly.dtsi              |  45 ++
 arch/arm/boot/dts/rk3288.dtsi                      |  45 ++
 drivers/gpu/drm/i2c/Kconfig                        |   6 +
 drivers/gpu/drm/i2c/Makefile                       |   2 +
 drivers/gpu/drm/i2c/vga-simple.c                   | 325 +++++++++
 drivers/gpu/drm/rockchip/Kconfig                   |   9 +
 drivers/gpu/drm/rockchip/Makefile                  |   1 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c        |  32 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |   2 +
 drivers/gpu/drm/rockchip/rockchip_lvds.c           | 756 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.h           | 107 +++
 include/drm/drm_crtc.h                             |   4 +
 15 files changed, 1431 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
 create mode 100644 Documentation/devicetree/bindings/video/rockchip-lvds.txt
 create mode 100644 drivers/gpu/drm/i2c/vga-simple.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.h

-- 
2.1.1

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

* [PATCH 01/11] drm/encoder: allow encoders to remember their of_node
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:32     ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, mark.yao-TNX95d0MmH7DzftRWevZcw
  Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w, djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Heiko Stuebner

Add an of_node field to struct drm_encoder to let encoders optionally
remember from which devicetree node they originated.

Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
 include/drm/drm_crtc.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index cc369f3..0448732 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -527,6 +527,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
@@ -543,6 +544,9 @@ struct drm_encoder_funcs {
  */
 struct drm_encoder {
 	struct drm_device *dev;
+#ifdef CONFIG_OF
+	struct device_node *of_node;
+#endif
 	struct list_head head;
 
 	struct drm_mode_object base;
-- 
2.1.1

--
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] 38+ messages in thread

* [PATCH 01/11] drm/encoder: allow encoders to remember their of_node
@ 2015-01-31 16:32     ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

Add an of_node field to struct drm_encoder to let encoders optionally
remember from which devicetree node they originated.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 include/drm/drm_crtc.h | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index cc369f3..0448732 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -527,6 +527,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
@@ -543,6 +544,9 @@ struct drm_encoder_funcs {
  */
 struct drm_encoder {
 	struct drm_device *dev;
+#ifdef CONFIG_OF
+	struct device_node *of_node;
+#endif
 	struct list_head head;
 
 	struct drm_mode_object base;
-- 
2.1.1

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

* [PATCH 02/11] drm: add bindings for simple vga encoders
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:32   ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: airlied, mark.yao
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, dri-devel,
	linux-rockchip, robh+dt, galak, linux-arm-kernel

Add the necessary devicetree binding document for simple vga encoders.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 .../devicetree/bindings/drm/i2c/vga-simple.txt         | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/drm/i2c/vga-simple.txt

diff --git a/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt b/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
new file mode 100644
index 0000000..271df9a
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
@@ -0,0 +1,18 @@
+Device-Tree bindings for generic vga encoders
+
+Required properties;
+  - compatible: must be "adi,adv7123"
+
+Optional properties:
+  - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
+  - enable-gpio: GPIO pin to enable or disable the encoder
+  - vaa-supply: regulator for the analog power supply
+
+Example:
+
+	adv7123: vga-encoder {
+		compatible = "adi,adv7123";
+		ddc-i2c-bus = <&i2c4>;
+		vaa-supply = <&vcc_io>;
+		enable-gpio = <&gpio0 17 GPIO_ACTIVE_HIGH>;
+	};
-- 
2.1.1

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

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

* [PATCH 02/11] drm: add bindings for simple vga encoders
@ 2015-01-31 16:32   ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

Add the necessary devicetree binding document for simple vga encoders.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 .../devicetree/bindings/drm/i2c/vga-simple.txt         | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/drm/i2c/vga-simple.txt

diff --git a/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt b/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
new file mode 100644
index 0000000..271df9a
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
@@ -0,0 +1,18 @@
+Device-Tree bindings for generic vga encoders
+
+Required properties;
+  - compatible: must be "adi,adv7123"
+
+Optional properties:
+  - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
+  - enable-gpio: GPIO pin to enable or disable the encoder
+  - vaa-supply: regulator for the analog power supply
+
+Example:
+
+	adv7123: vga-encoder {
+		compatible = "adi,adv7123";
+		ddc-i2c-bus = <&i2c4>;
+		vaa-supply = <&vcc_io>;
+		enable-gpio = <&gpio0 17 GPIO_ACTIVE_HIGH>;
+	};
-- 
2.1.1

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

* [PATCH 03/11] drm: add driver for simple vga encoders
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:32   ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: airlied, mark.yao
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, dri-devel,
	linux-rockchip, robh+dt, galak, linux-arm-kernel

There exist simple vga encoders without any type of management interface
and just maybe a simple gpio for turning it on or off. Examples for these
are the Analog Devices ADV7123, Chipsea CS7123 or Micronas SDA7123.

Add a generic encoder driver for those that can be used by drm drivers
using the component framework.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/i2c/Kconfig      |   6 +
 drivers/gpu/drm/i2c/Makefile     |   2 +
 drivers/gpu/drm/i2c/vga-simple.c | 325 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 333 insertions(+)
 create mode 100644 drivers/gpu/drm/i2c/vga-simple.c

diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
index 22c7ed6..319f2cb 100644
--- a/drivers/gpu/drm/i2c/Kconfig
+++ b/drivers/gpu/drm/i2c/Kconfig
@@ -31,4 +31,10 @@ config DRM_I2C_NXP_TDA998X
 	help
 	  Support for NXP Semiconductors TDA998X HDMI encoders.
 
+config DRM_VGA_SIMPLE
+	tristate "Generic simple vga encoder"
+	help
+	  Support for vga encoder chips without any special settings
+	  and at most a power-control gpio.
+
 endmenu
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
index 2c72eb5..858961f 100644
--- a/drivers/gpu/drm/i2c/Makefile
+++ b/drivers/gpu/drm/i2c/Makefile
@@ -10,3 +10,5 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
 
 tda998x-y := tda998x_drv.o
 obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
+
+obj-$(CONFIG_DRM_VGA_SIMPLE) += vga-simple.o
diff --git a/drivers/gpu/drm/i2c/vga-simple.c b/drivers/gpu/drm/i2c/vga-simple.c
new file mode 100644
index 0000000..bb9d19c
--- /dev/null
+++ b/drivers/gpu/drm/i2c/vga-simple.c
@@ -0,0 +1,325 @@
+/*
+ * 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 <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_simple(x) container_of(x, struct vga_simple, connector)
+#define encoder_to_vga_simple(x) container_of(x, struct vga_simple, encoder)
+
+struct vga_simple {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	struct device *dev;
+	struct i2c_adapter *ddc;
+
+	struct regulator *vaa_reg;
+	struct gpio_desc *enable_gpio;
+
+	struct mutex enable_lock;
+	bool enabled;
+};
+
+/*
+ * Connector functions
+ */
+
+enum drm_connector_status vga_simple_detect(struct drm_connector *connector,
+					    bool force)
+{
+	struct vga_simple *vga = connector_to_vga_simple(connector);
+
+	if (!vga->ddc)
+		return connector_status_unknown;
+
+	if (drm_probe_ddc(vga->ddc))
+		return connector_status_connected;
+
+	return connector_status_disconnected;
+}
+
+void vga_simple_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+struct drm_connector_funcs vga_simple_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = vga_simple_detect,
+	.destroy = vga_simple_connector_destroy,
+};
+
+/*
+ * Connector helper functions
+ */
+
+static int vga_simple_connector_get_modes(struct drm_connector *connector)
+{
+	struct vga_simple *vga = connector_to_vga_simple(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_simple_connector_mode_valid(struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder
+*vga_simple_connector_best_encoder(struct drm_connector *connector)
+{
+	struct vga_simple *vga = connector_to_vga_simple(connector);
+
+	return &vga->encoder;
+}
+
+static struct drm_connector_helper_funcs vga_simple_connector_helper_funcs = {
+	.get_modes = vga_simple_connector_get_modes,
+	.best_encoder = vga_simple_connector_best_encoder,
+	.mode_valid = vga_simple_connector_mode_valid,
+};
+
+/*
+ * Encoder functions
+ */
+
+static void vga_simple_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs vga_simple_encoder_funcs = {
+	.destroy = vga_simple_encoder_destroy,
+};
+
+/*
+ * Encoder helper functions
+ */
+
+static void vga_simple_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct vga_simple *vga = encoder_to_vga_simple(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->enable_gpio)
+			gpiod_set_value(vga->enable_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->enable_gpio)
+			gpiod_set_value(vga->enable_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_simple_mode_fixup(struct drm_encoder *encoder,
+				  const struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void vga_simple_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void vga_simple_encoder_mode_set(struct drm_encoder *encoder,
+					struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void vga_simple_encoder_commit(struct drm_encoder *encoder)
+{
+	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void vga_simple_encoder_disable(struct drm_encoder *encoder)
+{
+	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static const struct drm_encoder_helper_funcs vga_simple_encoder_helper_funcs = {
+	.dpms = vga_simple_encoder_dpms,
+	.mode_fixup = vga_simple_mode_fixup,
+	.prepare = vga_simple_encoder_prepare,
+	.mode_set = vga_simple_encoder_mode_set,
+	.commit = vga_simple_encoder_commit,
+	.disable = vga_simple_encoder_disable,
+};
+
+/*
+ * Component helper functions
+ */
+
+static int vga_simple_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+
+	struct device_node *ddc_node, *np = pdev->dev.of_node;
+	struct vga_simple *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->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+						   GPIOD_OUT_LOW);
+	if (IS_ERR(vga->enable_gpio)) {
+		ret = PTR_ERR(vga->enable_gpio);
+		dev_err(dev, "failed to request GPIO: %d\n", ret);
+		return ret;
+	}
+
+	vga->enabled = false;
+
+	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");
+	}
+
+	vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
+
+	vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, np);
+	vga->encoder.of_node = np;
+	vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
+				DRM_CONNECTOR_POLL_DISCONNECT;
+
+	drm_encoder_helper_add(&vga->encoder, &vga_simple_encoder_helper_funcs);
+	drm_encoder_init(drm, &vga->encoder, &vga_simple_encoder_funcs,
+			 DRM_MODE_ENCODER_DAC);
+
+	drm_connector_helper_add(&vga->connector,
+				 &vga_simple_connector_helper_funcs);
+	drm_connector_init(drm, &vga->connector, &vga_simple_connector_funcs,
+			   DRM_MODE_CONNECTOR_VGA);
+
+	drm_mode_connector_attach_encoder(&vga->connector, &vga->encoder);
+
+	return 0;
+}
+
+static void vga_simple_unbind(struct device *dev, struct device *master,
+				    void *data)
+{
+	struct vga_simple *vga = dev_get_drvdata(dev);
+
+	vga->connector.funcs->destroy(&vga->connector);
+	vga->encoder.funcs->destroy(&vga->encoder);
+}
+
+static const struct component_ops vga_simple_ops = {
+	.bind = vga_simple_bind,
+	.unbind = vga_simple_unbind,
+};
+
+static int vga_simple_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &vga_simple_ops);
+}
+
+static int vga_simple_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vga_simple_ops);
+
+	return 0;
+}
+
+static const struct of_device_id vga_simple_ids[] = {
+	{ .compatible = "adi,adv7123", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, vga_simple_ids);
+
+static struct platform_driver vga_simple_driver = {
+	.probe  = vga_simple_probe,
+	.remove = vga_simple_remove,
+	.driver = {
+		.name = "vga-simple",
+		.of_match_table = vga_simple_ids,
+	},
+};
+module_platform_driver(vga_simple_driver);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("Simple vga converter");
+MODULE_LICENSE("GPL");
-- 
2.1.1

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

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

* [PATCH 03/11] drm: add driver for simple vga encoders
@ 2015-01-31 16:32   ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

There exist simple vga encoders without any type of management interface
and just maybe a simple gpio for turning it on or off. Examples for these
are the Analog Devices ADV7123, Chipsea CS7123 or Micronas SDA7123.

Add a generic encoder driver for those that can be used by drm drivers
using the component framework.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/i2c/Kconfig      |   6 +
 drivers/gpu/drm/i2c/Makefile     |   2 +
 drivers/gpu/drm/i2c/vga-simple.c | 325 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 333 insertions(+)
 create mode 100644 drivers/gpu/drm/i2c/vga-simple.c

diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
index 22c7ed6..319f2cb 100644
--- a/drivers/gpu/drm/i2c/Kconfig
+++ b/drivers/gpu/drm/i2c/Kconfig
@@ -31,4 +31,10 @@ config DRM_I2C_NXP_TDA998X
 	help
 	  Support for NXP Semiconductors TDA998X HDMI encoders.
 
+config DRM_VGA_SIMPLE
+	tristate "Generic simple vga encoder"
+	help
+	  Support for vga encoder chips without any special settings
+	  and at most a power-control gpio.
+
 endmenu
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
index 2c72eb5..858961f 100644
--- a/drivers/gpu/drm/i2c/Makefile
+++ b/drivers/gpu/drm/i2c/Makefile
@@ -10,3 +10,5 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
 
 tda998x-y := tda998x_drv.o
 obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
+
+obj-$(CONFIG_DRM_VGA_SIMPLE) += vga-simple.o
diff --git a/drivers/gpu/drm/i2c/vga-simple.c b/drivers/gpu/drm/i2c/vga-simple.c
new file mode 100644
index 0000000..bb9d19c
--- /dev/null
+++ b/drivers/gpu/drm/i2c/vga-simple.c
@@ -0,0 +1,325 @@
+/*
+ * 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 <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_simple(x) container_of(x, struct vga_simple, connector)
+#define encoder_to_vga_simple(x) container_of(x, struct vga_simple, encoder)
+
+struct vga_simple {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	struct device *dev;
+	struct i2c_adapter *ddc;
+
+	struct regulator *vaa_reg;
+	struct gpio_desc *enable_gpio;
+
+	struct mutex enable_lock;
+	bool enabled;
+};
+
+/*
+ * Connector functions
+ */
+
+enum drm_connector_status vga_simple_detect(struct drm_connector *connector,
+					    bool force)
+{
+	struct vga_simple *vga = connector_to_vga_simple(connector);
+
+	if (!vga->ddc)
+		return connector_status_unknown;
+
+	if (drm_probe_ddc(vga->ddc))
+		return connector_status_connected;
+
+	return connector_status_disconnected;
+}
+
+void vga_simple_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+struct drm_connector_funcs vga_simple_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = vga_simple_detect,
+	.destroy = vga_simple_connector_destroy,
+};
+
+/*
+ * Connector helper functions
+ */
+
+static int vga_simple_connector_get_modes(struct drm_connector *connector)
+{
+	struct vga_simple *vga = connector_to_vga_simple(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_simple_connector_mode_valid(struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder
+*vga_simple_connector_best_encoder(struct drm_connector *connector)
+{
+	struct vga_simple *vga = connector_to_vga_simple(connector);
+
+	return &vga->encoder;
+}
+
+static struct drm_connector_helper_funcs vga_simple_connector_helper_funcs = {
+	.get_modes = vga_simple_connector_get_modes,
+	.best_encoder = vga_simple_connector_best_encoder,
+	.mode_valid = vga_simple_connector_mode_valid,
+};
+
+/*
+ * Encoder functions
+ */
+
+static void vga_simple_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs vga_simple_encoder_funcs = {
+	.destroy = vga_simple_encoder_destroy,
+};
+
+/*
+ * Encoder helper functions
+ */
+
+static void vga_simple_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct vga_simple *vga = encoder_to_vga_simple(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->enable_gpio)
+			gpiod_set_value(vga->enable_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->enable_gpio)
+			gpiod_set_value(vga->enable_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_simple_mode_fixup(struct drm_encoder *encoder,
+				  const struct drm_display_mode *mode,
+				  struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void vga_simple_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void vga_simple_encoder_mode_set(struct drm_encoder *encoder,
+					struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void vga_simple_encoder_commit(struct drm_encoder *encoder)
+{
+	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void vga_simple_encoder_disable(struct drm_encoder *encoder)
+{
+	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static const struct drm_encoder_helper_funcs vga_simple_encoder_helper_funcs = {
+	.dpms = vga_simple_encoder_dpms,
+	.mode_fixup = vga_simple_mode_fixup,
+	.prepare = vga_simple_encoder_prepare,
+	.mode_set = vga_simple_encoder_mode_set,
+	.commit = vga_simple_encoder_commit,
+	.disable = vga_simple_encoder_disable,
+};
+
+/*
+ * Component helper functions
+ */
+
+static int vga_simple_bind(struct device *dev, struct device *master,
+				 void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+
+	struct device_node *ddc_node, *np = pdev->dev.of_node;
+	struct vga_simple *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->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+						   GPIOD_OUT_LOW);
+	if (IS_ERR(vga->enable_gpio)) {
+		ret = PTR_ERR(vga->enable_gpio);
+		dev_err(dev, "failed to request GPIO: %d\n", ret);
+		return ret;
+	}
+
+	vga->enabled = false;
+
+	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");
+	}
+
+	vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
+
+	vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, np);
+	vga->encoder.of_node = np;
+	vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
+				DRM_CONNECTOR_POLL_DISCONNECT;
+
+	drm_encoder_helper_add(&vga->encoder, &vga_simple_encoder_helper_funcs);
+	drm_encoder_init(drm, &vga->encoder, &vga_simple_encoder_funcs,
+			 DRM_MODE_ENCODER_DAC);
+
+	drm_connector_helper_add(&vga->connector,
+				 &vga_simple_connector_helper_funcs);
+	drm_connector_init(drm, &vga->connector, &vga_simple_connector_funcs,
+			   DRM_MODE_CONNECTOR_VGA);
+
+	drm_mode_connector_attach_encoder(&vga->connector, &vga->encoder);
+
+	return 0;
+}
+
+static void vga_simple_unbind(struct device *dev, struct device *master,
+				    void *data)
+{
+	struct vga_simple *vga = dev_get_drvdata(dev);
+
+	vga->connector.funcs->destroy(&vga->connector);
+	vga->encoder.funcs->destroy(&vga->encoder);
+}
+
+static const struct component_ops vga_simple_ops = {
+	.bind = vga_simple_bind,
+	.unbind = vga_simple_unbind,
+};
+
+static int vga_simple_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &vga_simple_ops);
+}
+
+static int vga_simple_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vga_simple_ops);
+
+	return 0;
+}
+
+static const struct of_device_id vga_simple_ids[] = {
+	{ .compatible = "adi,adv7123", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, vga_simple_ids);
+
+static struct platform_driver vga_simple_driver = {
+	.probe  = vga_simple_probe,
+	.remove = vga_simple_remove,
+	.driver = {
+		.name = "vga-simple",
+		.of_match_table = vga_simple_ids,
+	},
+};
+module_platform_driver(vga_simple_driver);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("Simple vga converter");
+MODULE_LICENSE("GPL");
-- 
2.1.1

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

* [PATCH 04/11] dt-bindings: Add documentation for rockchip lvds
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:32     ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, mark.yao-TNX95d0MmH7DzftRWevZcw
  Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w, djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Yao,
	Heiko Stuebner

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

Add binding documentation for Rockchip SoC LVDS driver.

Signed-off-by: Mark Yao <yzq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
 .../devicetree/bindings/video/rockchip-lvds.txt    | 59 ++++++++++++++++++++++
 1 file changed, 59 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..1616d7e
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-lvds.txt
@@ -0,0 +1,59 @@
+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,panel: phandle to a panel node as described by
+	Documentation/devicetree/bindings/panel/*
+
+- 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.
+
+- ports: contain a port node with endpoint definitions as defined in
+	Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+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,panel = <&panel>;
+		rockchip,data-mapping = "jeida";
+		rockchip,data-width = <24>;
+		rockchip,output = "rgb";
+		ports {
+			lvds_in: port {
+				#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>;
+				};
+			};
+		};
+	};
-- 
2.1.1

--
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] 38+ messages in thread

* [PATCH 04/11] dt-bindings: Add documentation for rockchip lvds
@ 2015-01-31 16:32     ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: 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    | 59 ++++++++++++++++++++++
 1 file changed, 59 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..1616d7e
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/rockchip-lvds.txt
@@ -0,0 +1,59 @@
+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,panel: phandle to a panel node as described by
+	Documentation/devicetree/bindings/panel/*
+
+- 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.
+
+- ports: contain a port node with endpoint definitions as defined in
+	Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+	lvds: lvds at 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,panel = <&panel>;
+		rockchip,data-mapping = "jeida";
+		rockchip,data-width = <24>;
+		rockchip,output = "rgb";
+		ports {
+			lvds_in: port {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				lvds_in_vopb: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_lvds>;
+				};
+				lvds_in_vopl: endpoint at 1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_lvds>;
+				};
+			};
+		};
+	};
-- 
2.1.1

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

* [PATCH 05/11] drm/rockchip: Add support for Rockchip Soc LVDS
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:32   ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: airlied, mark.yao
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, Mark Yao,
	dri-devel, linux-rockchip, robh+dt, galak, linux-arm-kernel

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

This adds support for Rockchip soc lvds found on rk3288

Signed-off-by: Mark Yao <yzq@rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/rockchip/Kconfig         |   9 +
 drivers/gpu/drm/rockchip/Makefile        |   1 +
 drivers/gpu/drm/rockchip/rockchip_lvds.c | 629 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.h | 107 ++++++
 4 files changed, 746 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 1e84628..23d460b 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -26,3 +26,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..a01a3cb
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_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/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 platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm_dev = data;
+
+	struct resource *res;
+	struct rockchip_lvds *lvds;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	int ret, i;
+
+	const struct of_device_id *match;
+	struct device_node *panel_node;
+	const char *name;
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
+	if (!lvds)
+		return -ENOMEM;
+
+	lvds->dev = dev;
+	lvds->drm_dev = drm_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;
+		}
+	}
+
+	panel_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0);
+	if (panel_node) {
+		struct drm_panel *panel;
+
+		panel = of_drm_find_panel(panel_node);
+		of_node_put(panel_node);
+
+		if (!panel) {
+			ret = -EPROBE_DEFER;
+			goto err_unprepare_pclk;
+		}
+
+		lvds->panel = panel;
+	} else {
+		dev_err(&pdev->dev, "no panel node found\n");
+		ret = -EINVAL;
+		goto err_unprepare_pclk;
+	}
+
+	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) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		goto err_unprepare_pclk;
+	}
+
+	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) {
+		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) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	ret = drm_panel_attach(lvds->panel, connector);
+	if (ret) {
+		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);
+err_unprepare_pclk:
+	clk_unprepare(lvds->pclk);
+	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);
+	}
+
+	clk_unprepare(lvds->pclk);
+}
+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)
+{
+	return component_add(&pdev->dev, &rockchip_lvds_component_ops);
+}
+
+static int rockchip_lvds_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &rockchip_lvds_component_ops);
+
+	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@rock-chips.com>");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+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@rock-chips.com>
+ *      mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_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.1

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

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

* [PATCH 05/11] drm/rockchip: Add support for Rockchip Soc LVDS
@ 2015-01-31 16:32   ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

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

This adds support for Rockchip soc lvds found on rk3288

Signed-off-by: Mark Yao <yzq@rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/rockchip/Kconfig         |   9 +
 drivers/gpu/drm/rockchip/Makefile        |   1 +
 drivers/gpu/drm/rockchip/rockchip_lvds.c | 629 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_lvds.h | 107 ++++++
 4 files changed, 746 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 1e84628..23d460b 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -26,3 +26,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..a01a3cb
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      Mark Yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_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/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 platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm_dev = data;
+
+	struct resource *res;
+	struct rockchip_lvds *lvds;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	int ret, i;
+
+	const struct of_device_id *match;
+	struct device_node *panel_node;
+	const char *name;
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
+	if (!lvds)
+		return -ENOMEM;
+
+	lvds->dev = dev;
+	lvds->drm_dev = drm_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;
+		}
+	}
+
+	panel_node = of_parse_phandle(dev->of_node, "rockchip,panel", 0);
+	if (panel_node) {
+		struct drm_panel *panel;
+
+		panel = of_drm_find_panel(panel_node);
+		of_node_put(panel_node);
+
+		if (!panel) {
+			ret = -EPROBE_DEFER;
+			goto err_unprepare_pclk;
+		}
+
+		lvds->panel = panel;
+	} else {
+		dev_err(&pdev->dev, "no panel node found\n");
+		ret = -EINVAL;
+		goto err_unprepare_pclk;
+	}
+
+	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) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		goto err_unprepare_pclk;
+	}
+
+	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) {
+		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) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	ret = drm_panel_attach(lvds->panel, connector);
+	if (ret) {
+		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);
+err_unprepare_pclk:
+	clk_unprepare(lvds->pclk);
+	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);
+	}
+
+	clk_unprepare(lvds->pclk);
+}
+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)
+{
+	return component_add(&pdev->dev, &rockchip_lvds_component_ops);
+}
+
+static int rockchip_lvds_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &rockchip_lvds_component_ops);
+
+	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@rock-chips.com>");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+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@rock-chips.com>
+ *      mark yao <mark.yao@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _ROCKCHIP_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.1

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

* [PATCH 06/11] drm/rockchip: lvds: register a bridge when no panel is set
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:32   ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: airlied, mark.yao
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, dri-devel,
	linux-rockchip, robh+dt, galak, linux-arm-kernel

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, an encoder can attach to.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 .../devicetree/bindings/video/rockchip-lvds.txt    |   8 +-
 drivers/gpu/drm/rockchip/rockchip_lvds.c           | 167 ++++++++++++++++++---
 2 files changed, 153 insertions(+), 22 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/rockchip-lvds.txt b/Documentation/devicetree/bindings/video/rockchip-lvds.txt
index 1616d7e..6ab69b4 100644
--- a/Documentation/devicetree/bindings/video/rockchip-lvds.txt
+++ b/Documentation/devicetree/bindings/video/rockchip-lvds.txt
@@ -15,8 +15,6 @@ Required properties:
 - avdd3v3-supply: regulator phandle for 3.3V analog power
 
 - rockchip,grf: phandle to the general register files syscon
-- rockchip,panel: phandle to a panel node as described by
-	Documentation/devicetree/bindings/panel/*
 
 - rockchip,data-mapping: should be "vesa" or "jeida",
 	This describes how the color bits are laid out in the
@@ -28,6 +26,12 @@ Required properties:
 - ports: contain a port node with endpoint definitions as defined in
 	Documentation/devicetree/bindings/media/video-interfaces.txt.
 
+Optional properties:
+- rockchip,panel: phandle to a panel node as described by
+	Documentation/devicetree/bindings/panel/*
+	If no panel reference is set the lvds will act like a
+	bridge for other outbound interfaces.
+
 Example:
 	lvds: lvds@ff96c000 {
 		compatible = "rockchip,rk3288-lvds";
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index a01a3cb..46308fb 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -42,6 +42,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
  */
@@ -67,6 +70,7 @@ struct rockchip_lvds {
 	struct drm_panel *panel;
 	struct drm_connector connector;
 	struct drm_encoder encoder;
+	struct drm_bridge bridge;
 
 	struct mutex suspend_lock;
 	int suspend;
@@ -247,11 +251,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;
@@ -346,32 +349,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;
 	}
 }
@@ -404,6 +427,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 alled 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,
@@ -525,9 +639,20 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 
 		lvds->panel = panel;
 	} else {
-		dev_err(&pdev->dev, "no panel node found\n");
-		ret = -EINVAL;
-		goto err_unprepare_pclk;
+		/*
+		 * When no panel is found, register a bridge instead.
+		 * We expect the code handling external encoders to
+		 * connect encoder and bridge.
+		 */
+		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;
+		}
+
+		return 0;
 	}
 
 	encoder = &lvds->encoder;
@@ -592,6 +717,8 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 
 		drm_connector_cleanup(&lvds->connector);
 		drm_encoder_cleanup(&lvds->encoder);
+	} else {
+		drm_bridge_remove(&lvds->bridge);
 	}
 
 	clk_unprepare(lvds->pclk);
-- 
2.1.1

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

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

* [PATCH 06/11] drm/rockchip: lvds: register a bridge when no panel is set
@ 2015-01-31 16:32   ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:32 UTC (permalink / raw)
  To: linux-arm-kernel

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, an encoder can attach to.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 .../devicetree/bindings/video/rockchip-lvds.txt    |   8 +-
 drivers/gpu/drm/rockchip/rockchip_lvds.c           | 167 ++++++++++++++++++---
 2 files changed, 153 insertions(+), 22 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/rockchip-lvds.txt b/Documentation/devicetree/bindings/video/rockchip-lvds.txt
index 1616d7e..6ab69b4 100644
--- a/Documentation/devicetree/bindings/video/rockchip-lvds.txt
+++ b/Documentation/devicetree/bindings/video/rockchip-lvds.txt
@@ -15,8 +15,6 @@ Required properties:
 - avdd3v3-supply: regulator phandle for 3.3V analog power
 
 - rockchip,grf: phandle to the general register files syscon
-- rockchip,panel: phandle to a panel node as described by
-	Documentation/devicetree/bindings/panel/*
 
 - rockchip,data-mapping: should be "vesa" or "jeida",
 	This describes how the color bits are laid out in the
@@ -28,6 +26,12 @@ Required properties:
 - ports: contain a port node with endpoint definitions as defined in
 	Documentation/devicetree/bindings/media/video-interfaces.txt.
 
+Optional properties:
+- rockchip,panel: phandle to a panel node as described by
+	Documentation/devicetree/bindings/panel/*
+	If no panel reference is set the lvds will act like a
+	bridge for other outbound interfaces.
+
 Example:
 	lvds: lvds at ff96c000 {
 		compatible = "rockchip,rk3288-lvds";
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index a01a3cb..46308fb 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -42,6 +42,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
  */
@@ -67,6 +70,7 @@ struct rockchip_lvds {
 	struct drm_panel *panel;
 	struct drm_connector connector;
 	struct drm_encoder encoder;
+	struct drm_bridge bridge;
 
 	struct mutex suspend_lock;
 	int suspend;
@@ -247,11 +251,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;
@@ -346,32 +349,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;
 	}
 }
@@ -404,6 +427,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 alled 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,
@@ -525,9 +639,20 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 
 		lvds->panel = panel;
 	} else {
-		dev_err(&pdev->dev, "no panel node found\n");
-		ret = -EINVAL;
-		goto err_unprepare_pclk;
+		/*
+		 * When no panel is found, register a bridge instead.
+		 * We expect the code handling external encoders to
+		 * connect encoder and bridge.
+		 */
+		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;
+		}
+
+		return 0;
 	}
 
 	encoder = &lvds->encoder;
@@ -592,6 +717,8 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 
 		drm_connector_cleanup(&lvds->connector);
 		drm_encoder_cleanup(&lvds->encoder);
+	} else {
+		drm_bridge_remove(&lvds->bridge);
 	}
 
 	clk_unprepare(lvds->pclk);
-- 
2.1.1

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

* [PATCH 07/11] drm/rockchip: attach rgb bridge to encoders needing it
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:33     ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:33 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, mark.yao-TNX95d0MmH7DzftRWevZcw
  Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w, djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Heiko Stuebner

On SoCs like the rk3288 the lvds controller needs to be configured even for
just providing rgb data to an attached encoder. As internals of the
rockchip drm driver should not leak into the implementation of generic
i2c encoders etc, go through the list of encoders and attach any necessary
rgb bridges when building the drm device in the load callback.

Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
 .../devicetree/bindings/video/rockchip-vop.txt     | 16 +++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c        | 32 ++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/Documentation/devicetree/bindings/video/rockchip-vop.txt b/Documentation/devicetree/bindings/video/rockchip-vop.txt
index d15351f..b7762ed 100644
--- a/Documentation/devicetree/bindings/video/rockchip-vop.txt
+++ b/Documentation/devicetree/bindings/video/rockchip-vop.txt
@@ -32,6 +32,11 @@ Required properties:
 - port: A port node with endpoint definitions as defined in
   Documentation/devicetree/bindings/media/video-interfaces.txt.
 
+Optional properties in encoder nodes:
+- rockchip,rgb-bridge: if a separate controller is regulating access
+		to the rgb output interface it should be referenced
+		in the encoder node.
+
 Example:
 SoC specific DT entry:
 	vopb: vopb@ff930000 {
@@ -56,3 +61,14 @@ SoC specific DT entry:
 			};
 		};
 	};
+
+Additional entries when a rgb-bridge is used:
+
+	lvds: lvds@ff96c000 {
+		...
+	};
+
+	external-encoder {
+		...
+		rockchip,rgb-bridge = <&lvds>;
+	};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 30da781..86a3e61 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -130,6 +130,7 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
 	struct dma_iommu_mapping *mapping;
 	struct device *dev = drm_dev->dev;
 	struct drm_connector *connector;
+	struct drm_encoder *encoder;
 	int ret;
 
 	private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
@@ -173,6 +174,37 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
 		goto err_detach_device;
 
 	/*
+	 * Attach rgb bridge to encoders needing it.
+	 */
+	list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list, head) {
+		struct device_node *rgb_node;
+
+		if (!encoder->of_node)
+			continue;
+
+		rgb_node = of_parse_phandle(encoder->of_node,
+					    "rockchip,rgb-bridge", 0);
+		if (rgb_node) {
+			struct drm_bridge *bridge;
+
+			bridge = of_drm_find_bridge(rgb_node);
+			of_node_put(rgb_node);
+			if (!bridge) {
+				ret = -EPROBE_DEFER;
+				goto err_unbind;
+			}
+
+			encoder->bridge = bridge;
+			bridge->encoder = encoder;
+			ret = drm_bridge_attach(encoder->dev, bridge);
+			if (ret) {
+				DRM_ERROR("Failed to attach bridge to drm\n");
+				goto err_unbind;
+			}
+		}
+	}
+
+	/*
 	 * All components are now added, we can publish the connector sysfs
 	 * entries to userspace.  This will generate hotplug events and so
 	 * userspace will expect to be able to access DRM at this point.
-- 
2.1.1

--
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] 38+ messages in thread

* [PATCH 07/11] drm/rockchip: attach rgb bridge to encoders needing it
@ 2015-01-31 16:33     ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:33 UTC (permalink / raw)
  To: linux-arm-kernel

On SoCs like the rk3288 the lvds controller needs to be configured even for
just providing rgb data to an attached encoder. As internals of the
rockchip drm driver should not leak into the implementation of generic
i2c encoders etc, go through the list of encoders and attach any necessary
rgb bridges when building the drm device in the load callback.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 .../devicetree/bindings/video/rockchip-vop.txt     | 16 +++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c        | 32 ++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/Documentation/devicetree/bindings/video/rockchip-vop.txt b/Documentation/devicetree/bindings/video/rockchip-vop.txt
index d15351f..b7762ed 100644
--- a/Documentation/devicetree/bindings/video/rockchip-vop.txt
+++ b/Documentation/devicetree/bindings/video/rockchip-vop.txt
@@ -32,6 +32,11 @@ Required properties:
 - port: A port node with endpoint definitions as defined in
   Documentation/devicetree/bindings/media/video-interfaces.txt.
 
+Optional properties in encoder nodes:
+- rockchip,rgb-bridge: if a separate controller is regulating access
+		to the rgb output interface it should be referenced
+		in the encoder node.
+
 Example:
 SoC specific DT entry:
 	vopb: vopb at ff930000 {
@@ -56,3 +61,14 @@ SoC specific DT entry:
 			};
 		};
 	};
+
+Additional entries when a rgb-bridge is used:
+
+	lvds: lvds at ff96c000 {
+		...
+	};
+
+	external-encoder {
+		...
+		rockchip,rgb-bridge = <&lvds>;
+	};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 30da781..86a3e61 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -130,6 +130,7 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
 	struct dma_iommu_mapping *mapping;
 	struct device *dev = drm_dev->dev;
 	struct drm_connector *connector;
+	struct drm_encoder *encoder;
 	int ret;
 
 	private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
@@ -173,6 +174,37 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
 		goto err_detach_device;
 
 	/*
+	 * Attach rgb bridge to encoders needing it.
+	 */
+	list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list, head) {
+		struct device_node *rgb_node;
+
+		if (!encoder->of_node)
+			continue;
+
+		rgb_node = of_parse_phandle(encoder->of_node,
+					    "rockchip,rgb-bridge", 0);
+		if (rgb_node) {
+			struct drm_bridge *bridge;
+
+			bridge = of_drm_find_bridge(rgb_node);
+			of_node_put(rgb_node);
+			if (!bridge) {
+				ret = -EPROBE_DEFER;
+				goto err_unbind;
+			}
+
+			encoder->bridge = bridge;
+			bridge->encoder = encoder;
+			ret = drm_bridge_attach(encoder->dev, bridge);
+			if (ret) {
+				DRM_ERROR("Failed to attach bridge to drm\n");
+				goto err_unbind;
+			}
+		}
+	}
+
+	/*
 	 * All components are now added, we can publish the connector sysfs
 	 * entries to userspace.  This will generate hotplug events and so
 	 * userspace will expect to be able to access DRM at this point.
-- 
2.1.1

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

* [PATCH 08/11] drm/rockchip: enable rgb ouput of vops for vga and tv connectors
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:33   ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:33 UTC (permalink / raw)
  To: airlied, mark.yao
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, dri-devel,
	linux-rockchip, robh+dt, galak, linux-arm-kernel

The socs itself do not contain encoders for either vga or tv output.
Therefore these will be realized by external components and thus use the
rgb output.

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

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c0387f7..b744888 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -863,6 +863,8 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
 
 	switch (vop->connector_type) {
 	case DRM_MODE_CONNECTOR_LVDS:
+	case DRM_MODE_CONNECTOR_VGA:
+	case DRM_MODE_CONNECTOR_TV:
 		VOP_CTRL_SET(vop, rgb_en, 1);
 		break;
 	case DRM_MODE_CONNECTOR_eDP:
-- 
2.1.1

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

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

* [PATCH 08/11] drm/rockchip: enable rgb ouput of vops for vga and tv connectors
@ 2015-01-31 16:33   ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:33 UTC (permalink / raw)
  To: linux-arm-kernel

The socs itself do not contain encoders for either vga or tv output.
Therefore these will be realized by external components and thus use the
rgb output.

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

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c0387f7..b744888 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -863,6 +863,8 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
 
 	switch (vop->connector_type) {
 	case DRM_MODE_CONNECTOR_LVDS:
+	case DRM_MODE_CONNECTOR_VGA:
+	case DRM_MODE_CONNECTOR_TV:
 		VOP_CTRL_SET(vop, rgb_en, 1);
 		break;
 	case DRM_MODE_CONNECTOR_eDP:
-- 
2.1.1

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

* [PATCH 09/11] ARM: dts: rockchip: add rk3288 lcdc0 pinmux settings
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:33     ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:33 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, mark.yao-TNX95d0MmH7DzftRWevZcw
  Cc: robdclark-Re5JQEeQqe8AvxtiuMwx3w, djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	galak-sgV2jX0FEOL9JmXXK+q4OQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Heiko Stuebner

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-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
---
 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 d771f68..846d961 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -910,6 +910,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.1

--
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] 38+ messages in thread

* [PATCH 09/11] ARM: dts: rockchip: add rk3288 lcdc0 pinmux settings
@ 2015-01-31 16:33     ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:33 UTC (permalink / raw)
  To: 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 d771f68..846d961 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -910,6 +910,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.1

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

* [PATCH 10/11] ARM: dts: rockchip: add rk3288 lvds node
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:33   ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:33 UTC (permalink / raw)
  To: airlied, mark.yao
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, dri-devel,
	linux-rockchip, robh+dt, galak, 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 | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 846d961..d063892 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -615,6 +615,11 @@
 				reg = <0>;
 				remote-endpoint = <&hdmi_in_vopb>;
 			};
+
+			vopb_out_lvds: endpoint@1 {
+				reg = <1>;
+				remote-endpoint = <&lvds_in_vopb>;
+			};
 		};
 	};
 
@@ -646,6 +651,11 @@
 				reg = <0>;
 				remote-endpoint = <&hdmi_in_vopl>;
 			};
+
+			vopl_out_lvds: endpoint@1 {
+				reg = <1>;
+				remote-endpoint = <&lvds_in_vopl>;
+			};
 		};
 	};
 
@@ -658,6 +668,32 @@
 		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 {
+			lvds_in: port {
+				#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.1

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

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

* [PATCH 10/11] ARM: dts: rockchip: add rk3288 lvds node
@ 2015-01-31 16:33   ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:33 UTC (permalink / raw)
  To: 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 | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 846d961..d063892 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -615,6 +615,11 @@
 				reg = <0>;
 				remote-endpoint = <&hdmi_in_vopb>;
 			};
+
+			vopb_out_lvds: endpoint at 1 {
+				reg = <1>;
+				remote-endpoint = <&lvds_in_vopb>;
+			};
 		};
 	};
 
@@ -646,6 +651,11 @@
 				reg = <0>;
 				remote-endpoint = <&hdmi_in_vopl>;
 			};
+
+			vopl_out_lvds: endpoint at 1 {
+				reg = <1>;
+				remote-endpoint = <&lvds_in_vopl>;
+			};
 		};
 	};
 
@@ -658,6 +668,32 @@
 		status = "disabled";
 	};
 
+	lvds: lvds at 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 {
+			lvds_in: port {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				lvds_in_vopb: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_lvds>;
+				};
+				lvds_in_vopl: endpoint at 1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_lvds>;
+				};
+			};
+		};
+	};
+
 	hdmi: hdmi at ff980000 {
 		compatible = "rockchip,rk3288-dw-hdmi";
 		reg = <0xff980000 0x20000>;
-- 
2.1.1

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

* [PATCH 11/11] ARM: dts: rockchip: add vga encoder and enable lvds on rk3288-firefly
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-01-31 16:33   ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:33 UTC (permalink / raw)
  To: airlied, mark.yao
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, dri-devel,
	linux-rockchip, robh+dt, galak, 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 | 45 +++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index e6f873a..9d383ea 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -159,6 +159,27 @@
 		regulator-always-on;
 		vin-supply = <&vcc_5v>;
 	};
+
+	sda7123: vga-encoder {
+		compatible = "adi,adv7123";
+		ddc-i2c-bus = <&i2c4>;
+		enable-gpio = <&gpio0 17 GPIO_ACTIVE_HIGH>;
+		rockchip,rgb-bridge = <&lvds>;
+		vaa-supply = <&vcc_io>;
+
+		vga_in: port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			vga_in_vopb: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&vopb_out_vga>;
+			};
+			vga_in_vopl: endpoint@1 {
+				reg = <1>;
+				remote-endpoint = <&vopl_out_vga>;
+			};
+		};
+	};
 };
 
 &cpu0 {
@@ -331,6 +352,16 @@
 	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";
+};
+
 &pinctrl {
 	pcfg_output_high: pcfg-output-high {
 		output-high;
@@ -477,10 +508,24 @@
 	status = "okay";
 };
 
+&vopb_out {
+	vopb_out_vga: endpoint@9 {
+		reg = <9>;
+		remote-endpoint = <&vga_in_vopb>;
+	};
+};
+
 &vopl {
 	status = "okay";
 };
 
+&vopl_out {
+	vopl_out_vga: endpoint@9 {
+		reg = <9>;
+		remote-endpoint = <&vga_in_vopl>;
+	};
+};
+
 &vopl_mmu {
 	status = "okay";
 };
-- 
2.1.1

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

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

* [PATCH 11/11] ARM: dts: rockchip: add vga encoder and enable lvds on rk3288-firefly
@ 2015-01-31 16:33   ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-01-31 16:33 UTC (permalink / raw)
  To: 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 | 45 +++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index e6f873a..9d383ea 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -159,6 +159,27 @@
 		regulator-always-on;
 		vin-supply = <&vcc_5v>;
 	};
+
+	sda7123: vga-encoder {
+		compatible = "adi,adv7123";
+		ddc-i2c-bus = <&i2c4>;
+		enable-gpio = <&gpio0 17 GPIO_ACTIVE_HIGH>;
+		rockchip,rgb-bridge = <&lvds>;
+		vaa-supply = <&vcc_io>;
+
+		vga_in: port {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			vga_in_vopb: endpoint at 0 {
+				reg = <0>;
+				remote-endpoint = <&vopb_out_vga>;
+			};
+			vga_in_vopl: endpoint at 1 {
+				reg = <1>;
+				remote-endpoint = <&vopl_out_vga>;
+			};
+		};
+	};
 };
 
 &cpu0 {
@@ -331,6 +352,16 @@
 	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";
+};
+
 &pinctrl {
 	pcfg_output_high: pcfg-output-high {
 		output-high;
@@ -477,10 +508,24 @@
 	status = "okay";
 };
 
+&vopb_out {
+	vopb_out_vga: endpoint at 9 {
+		reg = <9>;
+		remote-endpoint = <&vga_in_vopb>;
+	};
+};
+
 &vopl {
 	status = "okay";
 };
 
+&vopl_out {
+	vopl_out_vga: endpoint at 9 {
+		reg = <9>;
+		remote-endpoint = <&vga_in_vopl>;
+	};
+};
+
 &vopl_mmu {
 	status = "okay";
 };
-- 
2.1.1

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

* Re: [PATCH 00/11] drm/rockchip: add support for lvds controller and external encoders
  2015-01-31 16:32 ` Heiko Stuebner
@ 2015-02-26  8:52   ` Heiko Stübner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stübner @ 2015-02-26  8:52 UTC (permalink / raw)
  To: airlied, mark.yao, robdclark
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, dri-devel,
	linux-rockchip, robh+dt, galak, linux-arm-kernel

Hi,

Am Samstag, 31. Januar 2015, 17:32:53 schrieb Heiko Stuebner:
> This series adds support for the lvds encoder present on rk3288 soc and
> allows external connectors to use the generic rgb pins.
> 
> On the older socs (rk3188, rk3066, etc) these pins where accessible by
> anyone, while on the rk3288 the lvds controller controls access to them.
> 
> So while on the old socs an external encoder was explicitly connected
> to one of the two lcd-controllers, on the rk3288 the lvds in between
> can toggle which controller should be the source.
> 
> To facilitate this the lvds encoder can use two modes. When a panel is
> attached it acts as encoder and without panel it just registers a bridge
> that can be used later.
> 
> The bridge association to an encoder is done via a rockchip,rgb-bridge
> property in the encoder node itself and handled in rockchip_drm_load
> to not leak rockchip-specific handling into generic encoder drivers.
> 
> 
> As example on how this can work, I've included a driver for simple
> (dumb) vga encoders, like the adv7123 (and clones) as used on the
> rk3288-firefly board. This same encoder is used on the Rayeager-px2
> board but there connected directly to the rgb pins of the rk3066
> which will hopefully also be supported in the future.
> 
> I've named this currently vga-simply (inspired by panel-simple), because
> so far I have found the adv7123 (and two clones) but I guess there will
> be more dumb vga encoders around that only differ in minimal things.
> 
> 
> Similarly, most of the rk3288-based TV-boxes use a rk1000 i2c tv encoder
> connected in a similar way - and again directly connected on the
> rk3188-radxarock.
> 
> 
> Caveats:
> - the i2c subdirectory is probably not the right one for my vga encoder
>   so if somebody could suggest where this should live, I'd be very happy
> - I'm not sure if I'm abusing some drm-APIs in a wrong way :-)

any comments or suggestions?

Especially on the core drm integration? Am I abusing stuff or does this look 
somewhat sane?


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

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

* [PATCH 00/11] drm/rockchip: add support for lvds controller and external encoders
@ 2015-02-26  8:52   ` Heiko Stübner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stübner @ 2015-02-26  8:52 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Am Samstag, 31. Januar 2015, 17:32:53 schrieb Heiko Stuebner:
> This series adds support for the lvds encoder present on rk3288 soc and
> allows external connectors to use the generic rgb pins.
> 
> On the older socs (rk3188, rk3066, etc) these pins where accessible by
> anyone, while on the rk3288 the lvds controller controls access to them.
> 
> So while on the old socs an external encoder was explicitly connected
> to one of the two lcd-controllers, on the rk3288 the lvds in between
> can toggle which controller should be the source.
> 
> To facilitate this the lvds encoder can use two modes. When a panel is
> attached it acts as encoder and without panel it just registers a bridge
> that can be used later.
> 
> The bridge association to an encoder is done via a rockchip,rgb-bridge
> property in the encoder node itself and handled in rockchip_drm_load
> to not leak rockchip-specific handling into generic encoder drivers.
> 
> 
> As example on how this can work, I've included a driver for simple
> (dumb) vga encoders, like the adv7123 (and clones) as used on the
> rk3288-firefly board. This same encoder is used on the Rayeager-px2
> board but there connected directly to the rgb pins of the rk3066
> which will hopefully also be supported in the future.
> 
> I've named this currently vga-simply (inspired by panel-simple), because
> so far I have found the adv7123 (and two clones) but I guess there will
> be more dumb vga encoders around that only differ in minimal things.
> 
> 
> Similarly, most of the rk3288-based TV-boxes use a rk1000 i2c tv encoder
> connected in a similar way - and again directly connected on the
> rk3188-radxarock.
> 
> 
> Caveats:
> - the i2c subdirectory is probably not the right one for my vga encoder
>   so if somebody could suggest where this should live, I'd be very happy
> - I'm not sure if I'm abusing some drm-APIs in a wrong way :-)

any comments or suggestions?

Especially on the core drm integration? Am I abusing stuff or does this look 
somewhat sane?


Thanks
Heiko

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

* Re: [PATCH 02/11] drm: add bindings for simple vga encoders
  2015-01-31 16:32   ` Heiko Stuebner
@ 2015-02-26 18:25     ` Laurent Pinchart
  -1 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2015-02-26 18:25 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree,
	linux-rockchip, robh+dt, galak, linux-arm-kernel

Hi Heiko,

Thank you for the patch.

On Saturday 31 January 2015 17:32:55 Heiko Stuebner wrote:
> Add the necessary devicetree binding document for simple vga encoders.
> 
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>  .../devicetree/bindings/drm/i2c/vga-simple.txt         | 18 +++++++++++++++
>  1 file changed, 18 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
> 
> diff --git a/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
> b/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt new file mode
> 100644
> index 0000000..271df9a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
> @@ -0,0 +1,18 @@
> +Device-Tree bindings for generic vga encoders
> +
> +Required properties;
> +  - compatible: must be "adi,adv7123"

Bindings already exist for that device, they have been added in commit 
8d0f1956f7c11202 ("video: Add ADV7123 DT bindings documentation").

> +Optional properties:
> +  - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
> +  - enable-gpio: GPIO pin to enable or disable the encoder
> +  - vaa-supply: regulator for the analog power supply
> +
> +Example:
> +
> +	adv7123: vga-encoder {
> +		compatible = "adi,adv7123";
> +		ddc-i2c-bus = <&i2c4>;
> +		vaa-supply = <&vcc_io>;
> +		enable-gpio = <&gpio0 17 GPIO_ACTIVE_HIGH>;
> +	};

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH 02/11] drm: add bindings for simple vga encoders
@ 2015-02-26 18:25     ` Laurent Pinchart
  0 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2015-02-26 18:25 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Heiko,

Thank you for the patch.

On Saturday 31 January 2015 17:32:55 Heiko Stuebner wrote:
> Add the necessary devicetree binding document for simple vga encoders.
> 
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>  .../devicetree/bindings/drm/i2c/vga-simple.txt         | 18 +++++++++++++++
>  1 file changed, 18 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
> 
> diff --git a/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
> b/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt new file mode
> 100644
> index 0000000..271df9a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/drm/i2c/vga-simple.txt
> @@ -0,0 +1,18 @@
> +Device-Tree bindings for generic vga encoders
> +
> +Required properties;
> +  - compatible: must be "adi,adv7123"

Bindings already exist for that device, they have been added in commit 
8d0f1956f7c11202 ("video: Add ADV7123 DT bindings documentation").

> +Optional properties:
> +  - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
> +  - enable-gpio: GPIO pin to enable or disable the encoder
> +  - vaa-supply: regulator for the analog power supply
> +
> +Example:
> +
> +	adv7123: vga-encoder {
> +		compatible = "adi,adv7123";
> +		ddc-i2c-bus = <&i2c4>;
> +		vaa-supply = <&vcc_io>;
> +		enable-gpio = <&gpio0 17 GPIO_ACTIVE_HIGH>;
> +	};

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 03/11] drm: add driver for simple vga encoders
  2015-01-31 16:32   ` Heiko Stuebner
@ 2015-02-26 18:33     ` Laurent Pinchart
  -1 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2015-02-26 18:33 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree,
	linux-rockchip, robh+dt, galak, linux-arm-kernel

Hi Heiko,

Thank you for the patch.

On Saturday 31 January 2015 17:32:56 Heiko Stuebner wrote:
> There exist simple vga encoders without any type of management interface
> and just maybe a simple gpio for turning it on or off. Examples for these
> are the Analog Devices ADV7123, Chipsea CS7123 or Micronas SDA7123.
> 
> Add a generic encoder driver for those that can be used by drm drivers
> using the component framework.

The rcar-du driver already handles the adi,adv7123 compatible string used by 
this driver. A generic driver is in my opinion a very good idea, but we can't 
break the existing support. Porting the rcar-du driver to the component model 
is needed.

> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>  drivers/gpu/drm/i2c/Kconfig      |   6 +
>  drivers/gpu/drm/i2c/Makefile     |   2 +
>  drivers/gpu/drm/i2c/vga-simple.c | 325 ++++++++++++++++++++++++++++++++++++

drivers/gpu/drm/i2c/ feels wrong for a platform device.

>  3 files changed, 333 insertions(+)
>  create mode 100644 drivers/gpu/drm/i2c/vga-simple.c
> 
> diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
> index 22c7ed6..319f2cb 100644
> --- a/drivers/gpu/drm/i2c/Kconfig
> +++ b/drivers/gpu/drm/i2c/Kconfig
> @@ -31,4 +31,10 @@ config DRM_I2C_NXP_TDA998X
>  	help
>  	  Support for NXP Semiconductors TDA998X HDMI encoders.
> 
> +config DRM_VGA_SIMPLE
> +	tristate "Generic simple vga encoder"
> +	help
> +	  Support for vga encoder chips without any special settings
> +	  and at most a power-control gpio.
> +
>  endmenu
> diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
> index 2c72eb5..858961f 100644
> --- a/drivers/gpu/drm/i2c/Makefile
> +++ b/drivers/gpu/drm/i2c/Makefile
> @@ -10,3 +10,5 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
> 
>  tda998x-y := tda998x_drv.o
>  obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
> +
> +obj-$(CONFIG_DRM_VGA_SIMPLE) += vga-simple.o
> diff --git a/drivers/gpu/drm/i2c/vga-simple.c
> b/drivers/gpu/drm/i2c/vga-simple.c new file mode 100644
> index 0000000..bb9d19c
> --- /dev/null
> +++ b/drivers/gpu/drm/i2c/vga-simple.c
> @@ -0,0 +1,325 @@
> +/*
> + * 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 <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_simple(x) container_of(x, struct vga_simple,
> connector) +#define encoder_to_vga_simple(x) container_of(x, struct
> vga_simple, encoder) +
> +struct vga_simple {
> +	struct drm_connector connector;
> +	struct drm_encoder encoder;
> +
> +	struct device *dev;
> +	struct i2c_adapter *ddc;
> +
> +	struct regulator *vaa_reg;
> +	struct gpio_desc *enable_gpio;
> +
> +	struct mutex enable_lock;
> +	bool enabled;
> +};
> +
> +/*
> + * Connector functions
> + */
> +
> +enum drm_connector_status vga_simple_detect(struct drm_connector
> *connector, +					    bool force)
> +{
> +	struct vga_simple *vga = connector_to_vga_simple(connector);
> +
> +	if (!vga->ddc)
> +		return connector_status_unknown;
> +
> +	if (drm_probe_ddc(vga->ddc))
> +		return connector_status_connected;
> +
> +	return connector_status_disconnected;
> +}
> +
> +void vga_simple_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +struct drm_connector_funcs vga_simple_connector_funcs = {
> +	.dpms = drm_helper_connector_dpms,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.detect = vga_simple_detect,
> +	.destroy = vga_simple_connector_destroy,
> +};
> +
> +/*
> + * Connector helper functions
> + */
> +
> +static int vga_simple_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct vga_simple *vga = connector_to_vga_simple(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_simple_connector_mode_valid(struct drm_connector *connector,
> +					struct drm_display_mode *mode)
> +{
> +	return MODE_OK;
> +}
> +
> +static struct drm_encoder
> +*vga_simple_connector_best_encoder(struct drm_connector *connector)
> +{
> +	struct vga_simple *vga = connector_to_vga_simple(connector);
> +
> +	return &vga->encoder;
> +}
> +
> +static struct drm_connector_helper_funcs vga_simple_connector_helper_funcs
> = { +	.get_modes = vga_simple_connector_get_modes,
> +	.best_encoder = vga_simple_connector_best_encoder,
> +	.mode_valid = vga_simple_connector_mode_valid,
> +};
> +
> +/*
> + * Encoder functions
> + */
> +
> +static void vga_simple_encoder_destroy(struct drm_encoder *encoder)
> +{
> +	drm_encoder_cleanup(encoder);
> +}
> +
> +static const struct drm_encoder_funcs vga_simple_encoder_funcs = {
> +	.destroy = vga_simple_encoder_destroy,
> +};
> +
> +/*
> + * Encoder helper functions
> + */
> +
> +static void vga_simple_encoder_dpms(struct drm_encoder *encoder, int mode)
> +{
> +	struct vga_simple *vga = encoder_to_vga_simple(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->enable_gpio)
> +			gpiod_set_value(vga->enable_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->enable_gpio)
> +			gpiod_set_value(vga->enable_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_simple_mode_fixup(struct drm_encoder *encoder,
> +				  const struct drm_display_mode *mode,
> +				  struct drm_display_mode *adjusted_mode)
> +{
> +	return true;
> +}
> +
> +static void vga_simple_encoder_prepare(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void vga_simple_encoder_mode_set(struct drm_encoder *encoder,
> +					struct drm_display_mode *mode,
> +					struct drm_display_mode *adjusted_mode)
> +{
> +}
> +
> +static void vga_simple_encoder_commit(struct drm_encoder *encoder)
> +{
> +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
> +}
> +
> +static void vga_simple_encoder_disable(struct drm_encoder *encoder)
> +{
> +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
> +}
> +
> +static const struct drm_encoder_helper_funcs
> vga_simple_encoder_helper_funcs = {
> +	.dpms = vga_simple_encoder_dpms,
> +	.mode_fixup = vga_simple_mode_fixup,
> +	.prepare = vga_simple_encoder_prepare,
> +	.mode_set = vga_simple_encoder_mode_set,
> +	.commit = vga_simple_encoder_commit,
> +	.disable = vga_simple_encoder_disable,

this is interesting. Some users of this encoder (I'm thinking about rcar-du, 
for which I've just posted the patches) are already ported to atomic updates, 
while others are not. How can we support both ?

> +};
> +
> +/*
> + * Component helper functions
> + */
> +
> +static int vga_simple_bind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +
> +	struct device_node *ddc_node, *np = pdev->dev.of_node;
> +	struct vga_simple *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->enable_gpio = devm_gpiod_get_optional(dev, "enable",
> +						   GPIOD_OUT_LOW);
> +	if (IS_ERR(vga->enable_gpio)) {
> +		ret = PTR_ERR(vga->enable_gpio);
> +		dev_err(dev, "failed to request GPIO: %d\n", ret);
> +		return ret;
> +	}
> +
> +	vga->enabled = false;
> +
> +	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");
> +	}
> +
> +	vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
> +
> +	vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, np);
> +	vga->encoder.of_node = np;
> +	vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
> +				DRM_CONNECTOR_POLL_DISCONNECT;
> +
> +	drm_encoder_helper_add(&vga->encoder, &vga_simple_encoder_helper_funcs);
> +	drm_encoder_init(drm, &vga->encoder, &vga_simple_encoder_funcs,
> +			 DRM_MODE_ENCODER_DAC);
> +
> +	drm_connector_helper_add(&vga->connector,
> +				 &vga_simple_connector_helper_funcs);

I really dislike this, this is an encoder driver, not a connector driver. It 
shouldn't be responsible for creating the connector. For all it knows, there 
might not even be a VGA connector connected to the encoder, VGA signals could 
be sent to a chained encoder. That might be a bit far-fetched in the VGA case, 
but in the generic case encoder drivers shouldn't create connectors.

> +	drm_connector_init(drm, &vga->connector, &vga_simple_connector_funcs,
> +			   DRM_MODE_CONNECTOR_VGA);
> +
> +	drm_mode_connector_attach_encoder(&vga->connector, &vga->encoder);
> +
> +	return 0;
> +}
> +
> +static void vga_simple_unbind(struct device *dev, struct device *master,
> +				    void *data)
> +{
> +	struct vga_simple *vga = dev_get_drvdata(dev);
> +
> +	vga->connector.funcs->destroy(&vga->connector);
> +	vga->encoder.funcs->destroy(&vga->encoder);
> +}
> +
> +static const struct component_ops vga_simple_ops = {
> +	.bind = vga_simple_bind,
> +	.unbind = vga_simple_unbind,
> +};
> +
> +static int vga_simple_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &vga_simple_ops);
> +}
> +
> +static int vga_simple_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vga_simple_ops);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id vga_simple_ids[] = {
> +	{ .compatible = "adi,adv7123", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, vga_simple_ids);
> +
> +static struct platform_driver vga_simple_driver = {
> +	.probe  = vga_simple_probe,
> +	.remove = vga_simple_remove,
> +	.driver = {
> +		.name = "vga-simple",
> +		.of_match_table = vga_simple_ids,
> +	},
> +};
> +module_platform_driver(vga_simple_driver);
> +
> +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
> +MODULE_DESCRIPTION("Simple vga converter");
> +MODULE_LICENSE("GPL");

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH 03/11] drm: add driver for simple vga encoders
@ 2015-02-26 18:33     ` Laurent Pinchart
  0 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2015-02-26 18:33 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Heiko,

Thank you for the patch.

On Saturday 31 January 2015 17:32:56 Heiko Stuebner wrote:
> There exist simple vga encoders without any type of management interface
> and just maybe a simple gpio for turning it on or off. Examples for these
> are the Analog Devices ADV7123, Chipsea CS7123 or Micronas SDA7123.
> 
> Add a generic encoder driver for those that can be used by drm drivers
> using the component framework.

The rcar-du driver already handles the adi,adv7123 compatible string used by 
this driver. A generic driver is in my opinion a very good idea, but we can't 
break the existing support. Porting the rcar-du driver to the component model 
is needed.

> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>  drivers/gpu/drm/i2c/Kconfig      |   6 +
>  drivers/gpu/drm/i2c/Makefile     |   2 +
>  drivers/gpu/drm/i2c/vga-simple.c | 325 ++++++++++++++++++++++++++++++++++++

drivers/gpu/drm/i2c/ feels wrong for a platform device.

>  3 files changed, 333 insertions(+)
>  create mode 100644 drivers/gpu/drm/i2c/vga-simple.c
> 
> diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
> index 22c7ed6..319f2cb 100644
> --- a/drivers/gpu/drm/i2c/Kconfig
> +++ b/drivers/gpu/drm/i2c/Kconfig
> @@ -31,4 +31,10 @@ config DRM_I2C_NXP_TDA998X
>  	help
>  	  Support for NXP Semiconductors TDA998X HDMI encoders.
> 
> +config DRM_VGA_SIMPLE
> +	tristate "Generic simple vga encoder"
> +	help
> +	  Support for vga encoder chips without any special settings
> +	  and at most a power-control gpio.
> +
>  endmenu
> diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
> index 2c72eb5..858961f 100644
> --- a/drivers/gpu/drm/i2c/Makefile
> +++ b/drivers/gpu/drm/i2c/Makefile
> @@ -10,3 +10,5 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
> 
>  tda998x-y := tda998x_drv.o
>  obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
> +
> +obj-$(CONFIG_DRM_VGA_SIMPLE) += vga-simple.o
> diff --git a/drivers/gpu/drm/i2c/vga-simple.c
> b/drivers/gpu/drm/i2c/vga-simple.c new file mode 100644
> index 0000000..bb9d19c
> --- /dev/null
> +++ b/drivers/gpu/drm/i2c/vga-simple.c
> @@ -0,0 +1,325 @@
> +/*
> + * 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 <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_simple(x) container_of(x, struct vga_simple,
> connector) +#define encoder_to_vga_simple(x) container_of(x, struct
> vga_simple, encoder) +
> +struct vga_simple {
> +	struct drm_connector connector;
> +	struct drm_encoder encoder;
> +
> +	struct device *dev;
> +	struct i2c_adapter *ddc;
> +
> +	struct regulator *vaa_reg;
> +	struct gpio_desc *enable_gpio;
> +
> +	struct mutex enable_lock;
> +	bool enabled;
> +};
> +
> +/*
> + * Connector functions
> + */
> +
> +enum drm_connector_status vga_simple_detect(struct drm_connector
> *connector, +					    bool force)
> +{
> +	struct vga_simple *vga = connector_to_vga_simple(connector);
> +
> +	if (!vga->ddc)
> +		return connector_status_unknown;
> +
> +	if (drm_probe_ddc(vga->ddc))
> +		return connector_status_connected;
> +
> +	return connector_status_disconnected;
> +}
> +
> +void vga_simple_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +struct drm_connector_funcs vga_simple_connector_funcs = {
> +	.dpms = drm_helper_connector_dpms,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.detect = vga_simple_detect,
> +	.destroy = vga_simple_connector_destroy,
> +};
> +
> +/*
> + * Connector helper functions
> + */
> +
> +static int vga_simple_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct vga_simple *vga = connector_to_vga_simple(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_simple_connector_mode_valid(struct drm_connector *connector,
> +					struct drm_display_mode *mode)
> +{
> +	return MODE_OK;
> +}
> +
> +static struct drm_encoder
> +*vga_simple_connector_best_encoder(struct drm_connector *connector)
> +{
> +	struct vga_simple *vga = connector_to_vga_simple(connector);
> +
> +	return &vga->encoder;
> +}
> +
> +static struct drm_connector_helper_funcs vga_simple_connector_helper_funcs
> = { +	.get_modes = vga_simple_connector_get_modes,
> +	.best_encoder = vga_simple_connector_best_encoder,
> +	.mode_valid = vga_simple_connector_mode_valid,
> +};
> +
> +/*
> + * Encoder functions
> + */
> +
> +static void vga_simple_encoder_destroy(struct drm_encoder *encoder)
> +{
> +	drm_encoder_cleanup(encoder);
> +}
> +
> +static const struct drm_encoder_funcs vga_simple_encoder_funcs = {
> +	.destroy = vga_simple_encoder_destroy,
> +};
> +
> +/*
> + * Encoder helper functions
> + */
> +
> +static void vga_simple_encoder_dpms(struct drm_encoder *encoder, int mode)
> +{
> +	struct vga_simple *vga = encoder_to_vga_simple(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->enable_gpio)
> +			gpiod_set_value(vga->enable_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->enable_gpio)
> +			gpiod_set_value(vga->enable_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_simple_mode_fixup(struct drm_encoder *encoder,
> +				  const struct drm_display_mode *mode,
> +				  struct drm_display_mode *adjusted_mode)
> +{
> +	return true;
> +}
> +
> +static void vga_simple_encoder_prepare(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void vga_simple_encoder_mode_set(struct drm_encoder *encoder,
> +					struct drm_display_mode *mode,
> +					struct drm_display_mode *adjusted_mode)
> +{
> +}
> +
> +static void vga_simple_encoder_commit(struct drm_encoder *encoder)
> +{
> +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
> +}
> +
> +static void vga_simple_encoder_disable(struct drm_encoder *encoder)
> +{
> +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
> +}
> +
> +static const struct drm_encoder_helper_funcs
> vga_simple_encoder_helper_funcs = {
> +	.dpms = vga_simple_encoder_dpms,
> +	.mode_fixup = vga_simple_mode_fixup,
> +	.prepare = vga_simple_encoder_prepare,
> +	.mode_set = vga_simple_encoder_mode_set,
> +	.commit = vga_simple_encoder_commit,
> +	.disable = vga_simple_encoder_disable,

this is interesting. Some users of this encoder (I'm thinking about rcar-du, 
for which I've just posted the patches) are already ported to atomic updates, 
while others are not. How can we support both ?

> +};
> +
> +/*
> + * Component helper functions
> + */
> +
> +static int vga_simple_bind(struct device *dev, struct device *master,
> +				 void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = data;
> +
> +	struct device_node *ddc_node, *np = pdev->dev.of_node;
> +	struct vga_simple *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->enable_gpio = devm_gpiod_get_optional(dev, "enable",
> +						   GPIOD_OUT_LOW);
> +	if (IS_ERR(vga->enable_gpio)) {
> +		ret = PTR_ERR(vga->enable_gpio);
> +		dev_err(dev, "failed to request GPIO: %d\n", ret);
> +		return ret;
> +	}
> +
> +	vga->enabled = false;
> +
> +	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");
> +	}
> +
> +	vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
> +
> +	vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, np);
> +	vga->encoder.of_node = np;
> +	vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
> +				DRM_CONNECTOR_POLL_DISCONNECT;
> +
> +	drm_encoder_helper_add(&vga->encoder, &vga_simple_encoder_helper_funcs);
> +	drm_encoder_init(drm, &vga->encoder, &vga_simple_encoder_funcs,
> +			 DRM_MODE_ENCODER_DAC);
> +
> +	drm_connector_helper_add(&vga->connector,
> +				 &vga_simple_connector_helper_funcs);

I really dislike this, this is an encoder driver, not a connector driver. It 
shouldn't be responsible for creating the connector. For all it knows, there 
might not even be a VGA connector connected to the encoder, VGA signals could 
be sent to a chained encoder. That might be a bit far-fetched in the VGA case, 
but in the generic case encoder drivers shouldn't create connectors.

> +	drm_connector_init(drm, &vga->connector, &vga_simple_connector_funcs,
> +			   DRM_MODE_CONNECTOR_VGA);
> +
> +	drm_mode_connector_attach_encoder(&vga->connector, &vga->encoder);
> +
> +	return 0;
> +}
> +
> +static void vga_simple_unbind(struct device *dev, struct device *master,
> +				    void *data)
> +{
> +	struct vga_simple *vga = dev_get_drvdata(dev);
> +
> +	vga->connector.funcs->destroy(&vga->connector);
> +	vga->encoder.funcs->destroy(&vga->encoder);
> +}
> +
> +static const struct component_ops vga_simple_ops = {
> +	.bind = vga_simple_bind,
> +	.unbind = vga_simple_unbind,
> +};
> +
> +static int vga_simple_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &vga_simple_ops);
> +}
> +
> +static int vga_simple_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vga_simple_ops);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id vga_simple_ids[] = {
> +	{ .compatible = "adi,adv7123", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, vga_simple_ids);
> +
> +static struct platform_driver vga_simple_driver = {
> +	.probe  = vga_simple_probe,
> +	.remove = vga_simple_remove,
> +	.driver = {
> +		.name = "vga-simple",
> +		.of_match_table = vga_simple_ids,
> +	},
> +};
> +module_platform_driver(vga_simple_driver);
> +
> +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
> +MODULE_DESCRIPTION("Simple vga converter");
> +MODULE_LICENSE("GPL");

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 04/11] dt-bindings: Add documentation for rockchip lvds
  2015-01-31 16:32     ` Heiko Stuebner
@ 2015-02-26 18:46       ` Laurent Pinchart
  -1 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2015-02-26 18:46 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, Mark Yao,
	robh+dt, galak, linux-rockchip, linux-arm-kernel

Hi Heiko,

Thank you for the patch.

On Saturday 31 January 2015 17:32:57 Heiko Stuebner wrote:
> 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    | 59 +++++++++++++++++++
>  1 file changed, 59 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..1616d7e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/video/rockchip-lvds.txt
> @@ -0,0 +1,59 @@
> +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,panel: phandle to a panel node as described by
> +	Documentation/devicetree/bindings/panel/*

Doesn't this duplicate the information provided by the ports ?

> +- 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.

Isn't all this panel-dependent ? Could we query the information from the panel 
at runtime instead ?

> +- ports: contain a port node with endpoint definitions as defined in
> +	Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +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,panel = <&panel>;
> +		rockchip,data-mapping = "jeida";
> +		rockchip,data-width = <24>;
> +		rockchip,output = "rgb";
> +		ports {
> +			lvds_in: port {
> +				#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>;
> +				};
> +			};
> +		};
> +	};

-- 
Regards,

Laurent Pinchart

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

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

* [PATCH 04/11] dt-bindings: Add documentation for rockchip lvds
@ 2015-02-26 18:46       ` Laurent Pinchart
  0 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2015-02-26 18:46 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Heiko,

Thank you for the patch.

On Saturday 31 January 2015 17:32:57 Heiko Stuebner wrote:
> 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    | 59 +++++++++++++++++++
>  1 file changed, 59 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..1616d7e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/video/rockchip-lvds.txt
> @@ -0,0 +1,59 @@
> +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,panel: phandle to a panel node as described by
> +	Documentation/devicetree/bindings/panel/*

Doesn't this duplicate the information provided by the ports ?

> +- 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.

Isn't all this panel-dependent ? Could we query the information from the panel 
at runtime instead ?

> +- ports: contain a port node with endpoint definitions as defined in
> +	Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Example:
> +	lvds: lvds at 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,panel = <&panel>;
> +		rockchip,data-mapping = "jeida";
> +		rockchip,data-width = <24>;
> +		rockchip,output = "rgb";
> +		ports {
> +			lvds_in: port {
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +				lvds_in_vopb: endpoint at 0 {
> +					reg = <0>;
> +					remote-endpoint = <&vopb_out_lvds>;
> +				};
> +				lvds_in_vopl: endpoint at 1 {
> +					reg = <1>;
> +					remote-endpoint = <&vopl_out_lvds>;
> +				};
> +			};
> +		};
> +	};

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH 03/11] drm: add driver for simple vga encoders
  2015-01-31 16:32   ` Heiko Stuebner
@ 2015-02-26 20:35       ` Rob Herring
  -1 siblings, 0 replies; 38+ messages in thread
From: Rob Herring @ 2015-02-26 20:35 UTC (permalink / raw)
  To: Heiko Stuebner
  Cc: David Airlie, mark.yao-TNX95d0MmH7DzftRWevZcw, Rob Clark,
	djkurtz-F7+t8E8rja9g9hUCZPvPmw,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Sat, Jan 31, 2015 at 10:32 AM, Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org> wrote:
> There exist simple vga encoders without any type of management interface
> and just maybe a simple gpio for turning it on or off. Examples for these
> are the Analog Devices ADV7123, Chipsea CS7123 or Micronas SDA7123.
>
> Add a generic encoder driver for those that can be used by drm drivers
> using the component framework.

What makes this specific to VGA other than DRM_MODE_CONNECTOR_VGA?
Seems like with match data you could generalize this to any simple
encoder chip.

Rob

>
> Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
> ---
>  drivers/gpu/drm/i2c/Kconfig      |   6 +
>  drivers/gpu/drm/i2c/Makefile     |   2 +
>  drivers/gpu/drm/i2c/vga-simple.c | 325 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 333 insertions(+)
>  create mode 100644 drivers/gpu/drm/i2c/vga-simple.c
>
> diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
> index 22c7ed6..319f2cb 100644
> --- a/drivers/gpu/drm/i2c/Kconfig
> +++ b/drivers/gpu/drm/i2c/Kconfig
> @@ -31,4 +31,10 @@ config DRM_I2C_NXP_TDA998X
>         help
>           Support for NXP Semiconductors TDA998X HDMI encoders.
>
> +config DRM_VGA_SIMPLE
> +       tristate "Generic simple vga encoder"
> +       help
> +         Support for vga encoder chips without any special settings
> +         and at most a power-control gpio.
> +
>  endmenu
> diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
> index 2c72eb5..858961f 100644
> --- a/drivers/gpu/drm/i2c/Makefile
> +++ b/drivers/gpu/drm/i2c/Makefile
> @@ -10,3 +10,5 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
>
>  tda998x-y := tda998x_drv.o
>  obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
> +
> +obj-$(CONFIG_DRM_VGA_SIMPLE) += vga-simple.o
> diff --git a/drivers/gpu/drm/i2c/vga-simple.c b/drivers/gpu/drm/i2c/vga-simple.c
> new file mode 100644
> index 0000000..bb9d19c
> --- /dev/null
> +++ b/drivers/gpu/drm/i2c/vga-simple.c
> @@ -0,0 +1,325 @@
> +/*
> + * 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 <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_simple(x) container_of(x, struct vga_simple, connector)
> +#define encoder_to_vga_simple(x) container_of(x, struct vga_simple, encoder)
> +
> +struct vga_simple {
> +       struct drm_connector connector;
> +       struct drm_encoder encoder;
> +
> +       struct device *dev;
> +       struct i2c_adapter *ddc;
> +
> +       struct regulator *vaa_reg;
> +       struct gpio_desc *enable_gpio;
> +
> +       struct mutex enable_lock;
> +       bool enabled;
> +};
> +
> +/*
> + * Connector functions
> + */
> +
> +enum drm_connector_status vga_simple_detect(struct drm_connector *connector,
> +                                           bool force)
> +{
> +       struct vga_simple *vga = connector_to_vga_simple(connector);
> +
> +       if (!vga->ddc)
> +               return connector_status_unknown;
> +
> +       if (drm_probe_ddc(vga->ddc))
> +               return connector_status_connected;
> +
> +       return connector_status_disconnected;
> +}
> +
> +void vga_simple_connector_destroy(struct drm_connector *connector)
> +{
> +       drm_connector_unregister(connector);
> +       drm_connector_cleanup(connector);
> +}
> +
> +struct drm_connector_funcs vga_simple_connector_funcs = {
> +       .dpms = drm_helper_connector_dpms,
> +       .fill_modes = drm_helper_probe_single_connector_modes,
> +       .detect = vga_simple_detect,
> +       .destroy = vga_simple_connector_destroy,
> +};
> +
> +/*
> + * Connector helper functions
> + */
> +
> +static int vga_simple_connector_get_modes(struct drm_connector *connector)
> +{
> +       struct vga_simple *vga = connector_to_vga_simple(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_simple_connector_mode_valid(struct drm_connector *connector,
> +                                       struct drm_display_mode *mode)
> +{
> +       return MODE_OK;
> +}
> +
> +static struct drm_encoder
> +*vga_simple_connector_best_encoder(struct drm_connector *connector)
> +{
> +       struct vga_simple *vga = connector_to_vga_simple(connector);
> +
> +       return &vga->encoder;
> +}
> +
> +static struct drm_connector_helper_funcs vga_simple_connector_helper_funcs = {
> +       .get_modes = vga_simple_connector_get_modes,
> +       .best_encoder = vga_simple_connector_best_encoder,
> +       .mode_valid = vga_simple_connector_mode_valid,
> +};
> +
> +/*
> + * Encoder functions
> + */
> +
> +static void vga_simple_encoder_destroy(struct drm_encoder *encoder)
> +{
> +       drm_encoder_cleanup(encoder);
> +}
> +
> +static const struct drm_encoder_funcs vga_simple_encoder_funcs = {
> +       .destroy = vga_simple_encoder_destroy,
> +};
> +
> +/*
> + * Encoder helper functions
> + */
> +
> +static void vga_simple_encoder_dpms(struct drm_encoder *encoder, int mode)
> +{
> +       struct vga_simple *vga = encoder_to_vga_simple(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->enable_gpio)
> +                       gpiod_set_value(vga->enable_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->enable_gpio)
> +                       gpiod_set_value(vga->enable_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_simple_mode_fixup(struct drm_encoder *encoder,
> +                                 const struct drm_display_mode *mode,
> +                                 struct drm_display_mode *adjusted_mode)
> +{
> +       return true;
> +}
> +
> +static void vga_simple_encoder_prepare(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void vga_simple_encoder_mode_set(struct drm_encoder *encoder,
> +                                       struct drm_display_mode *mode,
> +                                       struct drm_display_mode *adjusted_mode)
> +{
> +}
> +
> +static void vga_simple_encoder_commit(struct drm_encoder *encoder)
> +{
> +       vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
> +}
> +
> +static void vga_simple_encoder_disable(struct drm_encoder *encoder)
> +{
> +       vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
> +}
> +
> +static const struct drm_encoder_helper_funcs vga_simple_encoder_helper_funcs = {
> +       .dpms = vga_simple_encoder_dpms,
> +       .mode_fixup = vga_simple_mode_fixup,
> +       .prepare = vga_simple_encoder_prepare,
> +       .mode_set = vga_simple_encoder_mode_set,
> +       .commit = vga_simple_encoder_commit,
> +       .disable = vga_simple_encoder_disable,
> +};
> +
> +/*
> + * Component helper functions
> + */
> +
> +static int vga_simple_bind(struct device *dev, struct device *master,
> +                                void *data)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct drm_device *drm = data;
> +
> +       struct device_node *ddc_node, *np = pdev->dev.of_node;
> +       struct vga_simple *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->enable_gpio = devm_gpiod_get_optional(dev, "enable",
> +                                                  GPIOD_OUT_LOW);
> +       if (IS_ERR(vga->enable_gpio)) {
> +               ret = PTR_ERR(vga->enable_gpio);
> +               dev_err(dev, "failed to request GPIO: %d\n", ret);
> +               return ret;
> +       }
> +
> +       vga->enabled = false;
> +
> +       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");
> +       }
> +
> +       vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
> +
> +       vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, np);
> +       vga->encoder.of_node = np;
> +       vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
> +                               DRM_CONNECTOR_POLL_DISCONNECT;
> +
> +       drm_encoder_helper_add(&vga->encoder, &vga_simple_encoder_helper_funcs);
> +       drm_encoder_init(drm, &vga->encoder, &vga_simple_encoder_funcs,
> +                        DRM_MODE_ENCODER_DAC);
> +
> +       drm_connector_helper_add(&vga->connector,
> +                                &vga_simple_connector_helper_funcs);
> +       drm_connector_init(drm, &vga->connector, &vga_simple_connector_funcs,
> +                          DRM_MODE_CONNECTOR_VGA);
> +
> +       drm_mode_connector_attach_encoder(&vga->connector, &vga->encoder);
> +
> +       return 0;
> +}
> +
> +static void vga_simple_unbind(struct device *dev, struct device *master,
> +                                   void *data)
> +{
> +       struct vga_simple *vga = dev_get_drvdata(dev);
> +
> +       vga->connector.funcs->destroy(&vga->connector);
> +       vga->encoder.funcs->destroy(&vga->encoder);
> +}
> +
> +static const struct component_ops vga_simple_ops = {
> +       .bind = vga_simple_bind,
> +       .unbind = vga_simple_unbind,
> +};
> +
> +static int vga_simple_probe(struct platform_device *pdev)
> +{
> +       return component_add(&pdev->dev, &vga_simple_ops);
> +}
> +
> +static int vga_simple_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &vga_simple_ops);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id vga_simple_ids[] = {
> +       { .compatible = "adi,adv7123", },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, vga_simple_ids);
> +
> +static struct platform_driver vga_simple_driver = {
> +       .probe  = vga_simple_probe,
> +       .remove = vga_simple_remove,
> +       .driver = {
> +               .name = "vga-simple",
> +               .of_match_table = vga_simple_ids,
> +       },
> +};
> +module_platform_driver(vga_simple_driver);
> +
> +MODULE_AUTHOR("Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>");
> +MODULE_DESCRIPTION("Simple vga converter");
> +MODULE_LICENSE("GPL");
> --
> 2.1.1
>
--
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	[flat|nested] 38+ messages in thread

* [PATCH 03/11] drm: add driver for simple vga encoders
@ 2015-02-26 20:35       ` Rob Herring
  0 siblings, 0 replies; 38+ messages in thread
From: Rob Herring @ 2015-02-26 20:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Jan 31, 2015 at 10:32 AM, Heiko Stuebner <heiko@sntech.de> wrote:
> There exist simple vga encoders without any type of management interface
> and just maybe a simple gpio for turning it on or off. Examples for these
> are the Analog Devices ADV7123, Chipsea CS7123 or Micronas SDA7123.
>
> Add a generic encoder driver for those that can be used by drm drivers
> using the component framework.

What makes this specific to VGA other than DRM_MODE_CONNECTOR_VGA?
Seems like with match data you could generalize this to any simple
encoder chip.

Rob

>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>  drivers/gpu/drm/i2c/Kconfig      |   6 +
>  drivers/gpu/drm/i2c/Makefile     |   2 +
>  drivers/gpu/drm/i2c/vga-simple.c | 325 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 333 insertions(+)
>  create mode 100644 drivers/gpu/drm/i2c/vga-simple.c
>
> diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
> index 22c7ed6..319f2cb 100644
> --- a/drivers/gpu/drm/i2c/Kconfig
> +++ b/drivers/gpu/drm/i2c/Kconfig
> @@ -31,4 +31,10 @@ config DRM_I2C_NXP_TDA998X
>         help
>           Support for NXP Semiconductors TDA998X HDMI encoders.
>
> +config DRM_VGA_SIMPLE
> +       tristate "Generic simple vga encoder"
> +       help
> +         Support for vga encoder chips without any special settings
> +         and at most a power-control gpio.
> +
>  endmenu
> diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
> index 2c72eb5..858961f 100644
> --- a/drivers/gpu/drm/i2c/Makefile
> +++ b/drivers/gpu/drm/i2c/Makefile
> @@ -10,3 +10,5 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
>
>  tda998x-y := tda998x_drv.o
>  obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
> +
> +obj-$(CONFIG_DRM_VGA_SIMPLE) += vga-simple.o
> diff --git a/drivers/gpu/drm/i2c/vga-simple.c b/drivers/gpu/drm/i2c/vga-simple.c
> new file mode 100644
> index 0000000..bb9d19c
> --- /dev/null
> +++ b/drivers/gpu/drm/i2c/vga-simple.c
> @@ -0,0 +1,325 @@
> +/*
> + * 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 <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_simple(x) container_of(x, struct vga_simple, connector)
> +#define encoder_to_vga_simple(x) container_of(x, struct vga_simple, encoder)
> +
> +struct vga_simple {
> +       struct drm_connector connector;
> +       struct drm_encoder encoder;
> +
> +       struct device *dev;
> +       struct i2c_adapter *ddc;
> +
> +       struct regulator *vaa_reg;
> +       struct gpio_desc *enable_gpio;
> +
> +       struct mutex enable_lock;
> +       bool enabled;
> +};
> +
> +/*
> + * Connector functions
> + */
> +
> +enum drm_connector_status vga_simple_detect(struct drm_connector *connector,
> +                                           bool force)
> +{
> +       struct vga_simple *vga = connector_to_vga_simple(connector);
> +
> +       if (!vga->ddc)
> +               return connector_status_unknown;
> +
> +       if (drm_probe_ddc(vga->ddc))
> +               return connector_status_connected;
> +
> +       return connector_status_disconnected;
> +}
> +
> +void vga_simple_connector_destroy(struct drm_connector *connector)
> +{
> +       drm_connector_unregister(connector);
> +       drm_connector_cleanup(connector);
> +}
> +
> +struct drm_connector_funcs vga_simple_connector_funcs = {
> +       .dpms = drm_helper_connector_dpms,
> +       .fill_modes = drm_helper_probe_single_connector_modes,
> +       .detect = vga_simple_detect,
> +       .destroy = vga_simple_connector_destroy,
> +};
> +
> +/*
> + * Connector helper functions
> + */
> +
> +static int vga_simple_connector_get_modes(struct drm_connector *connector)
> +{
> +       struct vga_simple *vga = connector_to_vga_simple(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_simple_connector_mode_valid(struct drm_connector *connector,
> +                                       struct drm_display_mode *mode)
> +{
> +       return MODE_OK;
> +}
> +
> +static struct drm_encoder
> +*vga_simple_connector_best_encoder(struct drm_connector *connector)
> +{
> +       struct vga_simple *vga = connector_to_vga_simple(connector);
> +
> +       return &vga->encoder;
> +}
> +
> +static struct drm_connector_helper_funcs vga_simple_connector_helper_funcs = {
> +       .get_modes = vga_simple_connector_get_modes,
> +       .best_encoder = vga_simple_connector_best_encoder,
> +       .mode_valid = vga_simple_connector_mode_valid,
> +};
> +
> +/*
> + * Encoder functions
> + */
> +
> +static void vga_simple_encoder_destroy(struct drm_encoder *encoder)
> +{
> +       drm_encoder_cleanup(encoder);
> +}
> +
> +static const struct drm_encoder_funcs vga_simple_encoder_funcs = {
> +       .destroy = vga_simple_encoder_destroy,
> +};
> +
> +/*
> + * Encoder helper functions
> + */
> +
> +static void vga_simple_encoder_dpms(struct drm_encoder *encoder, int mode)
> +{
> +       struct vga_simple *vga = encoder_to_vga_simple(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->enable_gpio)
> +                       gpiod_set_value(vga->enable_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->enable_gpio)
> +                       gpiod_set_value(vga->enable_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_simple_mode_fixup(struct drm_encoder *encoder,
> +                                 const struct drm_display_mode *mode,
> +                                 struct drm_display_mode *adjusted_mode)
> +{
> +       return true;
> +}
> +
> +static void vga_simple_encoder_prepare(struct drm_encoder *encoder)
> +{
> +}
> +
> +static void vga_simple_encoder_mode_set(struct drm_encoder *encoder,
> +                                       struct drm_display_mode *mode,
> +                                       struct drm_display_mode *adjusted_mode)
> +{
> +}
> +
> +static void vga_simple_encoder_commit(struct drm_encoder *encoder)
> +{
> +       vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
> +}
> +
> +static void vga_simple_encoder_disable(struct drm_encoder *encoder)
> +{
> +       vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
> +}
> +
> +static const struct drm_encoder_helper_funcs vga_simple_encoder_helper_funcs = {
> +       .dpms = vga_simple_encoder_dpms,
> +       .mode_fixup = vga_simple_mode_fixup,
> +       .prepare = vga_simple_encoder_prepare,
> +       .mode_set = vga_simple_encoder_mode_set,
> +       .commit = vga_simple_encoder_commit,
> +       .disable = vga_simple_encoder_disable,
> +};
> +
> +/*
> + * Component helper functions
> + */
> +
> +static int vga_simple_bind(struct device *dev, struct device *master,
> +                                void *data)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct drm_device *drm = data;
> +
> +       struct device_node *ddc_node, *np = pdev->dev.of_node;
> +       struct vga_simple *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->enable_gpio = devm_gpiod_get_optional(dev, "enable",
> +                                                  GPIOD_OUT_LOW);
> +       if (IS_ERR(vga->enable_gpio)) {
> +               ret = PTR_ERR(vga->enable_gpio);
> +               dev_err(dev, "failed to request GPIO: %d\n", ret);
> +               return ret;
> +       }
> +
> +       vga->enabled = false;
> +
> +       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");
> +       }
> +
> +       vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
> +
> +       vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, np);
> +       vga->encoder.of_node = np;
> +       vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
> +                               DRM_CONNECTOR_POLL_DISCONNECT;
> +
> +       drm_encoder_helper_add(&vga->encoder, &vga_simple_encoder_helper_funcs);
> +       drm_encoder_init(drm, &vga->encoder, &vga_simple_encoder_funcs,
> +                        DRM_MODE_ENCODER_DAC);
> +
> +       drm_connector_helper_add(&vga->connector,
> +                                &vga_simple_connector_helper_funcs);
> +       drm_connector_init(drm, &vga->connector, &vga_simple_connector_funcs,
> +                          DRM_MODE_CONNECTOR_VGA);
> +
> +       drm_mode_connector_attach_encoder(&vga->connector, &vga->encoder);
> +
> +       return 0;
> +}
> +
> +static void vga_simple_unbind(struct device *dev, struct device *master,
> +                                   void *data)
> +{
> +       struct vga_simple *vga = dev_get_drvdata(dev);
> +
> +       vga->connector.funcs->destroy(&vga->connector);
> +       vga->encoder.funcs->destroy(&vga->encoder);
> +}
> +
> +static const struct component_ops vga_simple_ops = {
> +       .bind = vga_simple_bind,
> +       .unbind = vga_simple_unbind,
> +};
> +
> +static int vga_simple_probe(struct platform_device *pdev)
> +{
> +       return component_add(&pdev->dev, &vga_simple_ops);
> +}
> +
> +static int vga_simple_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &vga_simple_ops);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id vga_simple_ids[] = {
> +       { .compatible = "adi,adv7123", },
> +       {},
> +};
> +MODULE_DEVICE_TABLE(of, vga_simple_ids);
> +
> +static struct platform_driver vga_simple_driver = {
> +       .probe  = vga_simple_probe,
> +       .remove = vga_simple_remove,
> +       .driver = {
> +               .name = "vga-simple",
> +               .of_match_table = vga_simple_ids,
> +       },
> +};
> +module_platform_driver(vga_simple_driver);
> +
> +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
> +MODULE_DESCRIPTION("Simple vga converter");
> +MODULE_LICENSE("GPL");
> --
> 2.1.1
>

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

* Re: [PATCH 03/11] drm: add driver for simple vga encoders
  2015-02-26 18:33     ` Laurent Pinchart
@ 2015-02-28  0:42       ` Heiko Stübner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stübner @ 2015-02-28  0:42 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: mark.rutland, devicetree, pawel.moll, ijc+devicetree, dri-devel,
	linux-rockchip, robh+dt, galak, linux-arm-kernel

Hi Laurent,

thanks for the comments

Am Donnerstag, 26. Februar 2015, 20:33:33 schrieb Laurent Pinchart:
> On Saturday 31 January 2015 17:32:56 Heiko Stuebner wrote:
> > There exist simple vga encoders without any type of management interface
> > and just maybe a simple gpio for turning it on or off. Examples for these
> > are the Analog Devices ADV7123, Chipsea CS7123 or Micronas SDA7123.
> > 
> > Add a generic encoder driver for those that can be used by drm drivers
> > using the component framework.
> 
> The rcar-du driver already handles the adi,adv7123 compatible string used by
> this driver. A generic driver is in my opinion a very good idea, but we
> can't break the existing support. Porting the rcar-du driver to the
> component model is needed.

is this in someones plans already? Because I'm still having trouble 
understanding parts of the rockchip driver sometimes, so feel in no way 
qualified to try to get this tackled in some reasonable timeframe :-)


> > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > ---
> > 
> >  drivers/gpu/drm/i2c/Kconfig      |   6 +
> >  drivers/gpu/drm/i2c/Makefile     |   2 +
> >  drivers/gpu/drm/i2c/vga-simple.c | 325
> >  ++++++++++++++++++++++++++++++++++++
> 
> drivers/gpu/drm/i2c/ feels wrong for a platform device.

yep, i2c probably being the wrong place was one of the caveats in the cover 
letter and I was hoping some more seasoned drm people could suggest where this 
should live after all.

As your comments further down suggest that we might introduce some different 
components, simply congregate them in a "components" subdir or something 
splitting them more?


> 
> >  3 files changed, 333 insertions(+)
> >  create mode 100644 drivers/gpu/drm/i2c/vga-simple.c
> > 
> > diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
> > index 22c7ed6..319f2cb 100644
> > --- a/drivers/gpu/drm/i2c/Kconfig
> > +++ b/drivers/gpu/drm/i2c/Kconfig
> > @@ -31,4 +31,10 @@ config DRM_I2C_NXP_TDA998X
> > 
> >  	help
> >  	
> >  	  Support for NXP Semiconductors TDA998X HDMI encoders.
> > 
> > +config DRM_VGA_SIMPLE
> > +	tristate "Generic simple vga encoder"
> > +	help
> > +	  Support for vga encoder chips without any special settings
> > +	  and at most a power-control gpio.
> > +
> > 
> >  endmenu
> > 
> > diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
> > index 2c72eb5..858961f 100644
> > --- a/drivers/gpu/drm/i2c/Makefile
> > +++ b/drivers/gpu/drm/i2c/Makefile
> > @@ -10,3 +10,5 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
> > 
> >  tda998x-y := tda998x_drv.o
> >  obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
> > 
> > +
> > +obj-$(CONFIG_DRM_VGA_SIMPLE) += vga-simple.o
> > diff --git a/drivers/gpu/drm/i2c/vga-simple.c
> > b/drivers/gpu/drm/i2c/vga-simple.c new file mode 100644
> > index 0000000..bb9d19c
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i2c/vga-simple.c
> > @@ -0,0 +1,325 @@
> > +/*
> > + * 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 <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_simple(x) container_of(x, struct vga_simple,
> > connector) +#define encoder_to_vga_simple(x) container_of(x, struct
> > vga_simple, encoder) +
> > +struct vga_simple {
> > +	struct drm_connector connector;
> > +	struct drm_encoder encoder;
> > +
> > +	struct device *dev;
> > +	struct i2c_adapter *ddc;
> > +
> > +	struct regulator *vaa_reg;
> > +	struct gpio_desc *enable_gpio;
> > +
> > +	struct mutex enable_lock;
> > +	bool enabled;
> > +};
> > +
> > +/*
> > + * Connector functions
> > + */
> > +
> > +enum drm_connector_status vga_simple_detect(struct drm_connector
> > *connector, +					    bool force)
> > +{
> > +	struct vga_simple *vga = connector_to_vga_simple(connector);
> > +
> > +	if (!vga->ddc)
> > +		return connector_status_unknown;
> > +
> > +	if (drm_probe_ddc(vga->ddc))
> > +		return connector_status_connected;
> > +
> > +	return connector_status_disconnected;
> > +}
> > +
> > +void vga_simple_connector_destroy(struct drm_connector *connector)
> > +{
> > +	drm_connector_unregister(connector);
> > +	drm_connector_cleanup(connector);
> > +}
> > +
> > +struct drm_connector_funcs vga_simple_connector_funcs = {
> > +	.dpms = drm_helper_connector_dpms,
> > +	.fill_modes = drm_helper_probe_single_connector_modes,
> > +	.detect = vga_simple_detect,
> > +	.destroy = vga_simple_connector_destroy,
> > +};
> > +
> > +/*
> > + * Connector helper functions
> > + */
> > +
> > +static int vga_simple_connector_get_modes(struct drm_connector
> > *connector)
> > +{
> > +	struct vga_simple *vga = connector_to_vga_simple(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_simple_connector_mode_valid(struct drm_connector
> > *connector, +					struct drm_display_mode *mode)
> > +{
> > +	return MODE_OK;
> > +}
> > +
> > +static struct drm_encoder
> > +*vga_simple_connector_best_encoder(struct drm_connector *connector)
> > +{
> > +	struct vga_simple *vga = connector_to_vga_simple(connector);
> > +
> > +	return &vga->encoder;
> > +}
> > +
> > +static struct drm_connector_helper_funcs
> > vga_simple_connector_helper_funcs
> > = { +	.get_modes = vga_simple_connector_get_modes,
> > +	.best_encoder = vga_simple_connector_best_encoder,
> > +	.mode_valid = vga_simple_connector_mode_valid,
> > +};
> > +
> > +/*
> > + * Encoder functions
> > + */
> > +
> > +static void vga_simple_encoder_destroy(struct drm_encoder *encoder)
> > +{
> > +	drm_encoder_cleanup(encoder);
> > +}
> > +
> > +static const struct drm_encoder_funcs vga_simple_encoder_funcs = {
> > +	.destroy = vga_simple_encoder_destroy,
> > +};
> > +
> > +/*
> > + * Encoder helper functions
> > + */
> > +
> > +static void vga_simple_encoder_dpms(struct drm_encoder *encoder, int
> > mode)
> > +{
> > +	struct vga_simple *vga = encoder_to_vga_simple(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->enable_gpio)
> > +			gpiod_set_value(vga->enable_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->enable_gpio)
> > +			gpiod_set_value(vga->enable_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_simple_mode_fixup(struct drm_encoder *encoder,
> > +				  const struct drm_display_mode *mode,
> > +				  struct drm_display_mode *adjusted_mode)
> > +{
> > +	return true;
> > +}
> > +
> > +static void vga_simple_encoder_prepare(struct drm_encoder *encoder)
> > +{
> > +}
> > +
> > +static void vga_simple_encoder_mode_set(struct drm_encoder *encoder,
> > +					struct drm_display_mode *mode,
> > +					struct drm_display_mode *adjusted_mode)
> > +{
> > +}
> > +
> > +static void vga_simple_encoder_commit(struct drm_encoder *encoder)
> > +{
> > +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
> > +}
> > +
> > +static void vga_simple_encoder_disable(struct drm_encoder *encoder)
> > +{
> > +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
> > +}
> > +
> > +static const struct drm_encoder_helper_funcs
> > vga_simple_encoder_helper_funcs = {
> > +	.dpms = vga_simple_encoder_dpms,
> > +	.mode_fixup = vga_simple_mode_fixup,
> > +	.prepare = vga_simple_encoder_prepare,
> > +	.mode_set = vga_simple_encoder_mode_set,
> > +	.commit = vga_simple_encoder_commit,
> > +	.disable = vga_simple_encoder_disable,
> 
> this is interesting. Some users of this encoder (I'm thinking about rcar-du,
> for which I've just posted the patches) are already ported to atomic
> updates, while others are not. How can we support both ?

Wild guess, support both in such generic components and let bind decide which 
to use ... is there some sort of drm_is_atomic() against the master device.


> 
> > +};
> > +
> > +/*
> > + * Component helper functions
> > + */
> > +
> > +static int vga_simple_bind(struct device *dev, struct device *master,
> > +				 void *data)
> > +{
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	struct drm_device *drm = data;
> > +
> > +	struct device_node *ddc_node, *np = pdev->dev.of_node;
> > +	struct vga_simple *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->enable_gpio = devm_gpiod_get_optional(dev, "enable",
> > +						   GPIOD_OUT_LOW);
> > +	if (IS_ERR(vga->enable_gpio)) {
> > +		ret = PTR_ERR(vga->enable_gpio);
> > +		dev_err(dev, "failed to request GPIO: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	vga->enabled = false;
> > +
> > +	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");
> > +	}
> > +
> > +	vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
> > +
> > +	vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, np);
> > +	vga->encoder.of_node = np;
> > +	vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
> > +				DRM_CONNECTOR_POLL_DISCONNECT;
> > +
> > +	drm_encoder_helper_add(&vga->encoder, 
&vga_simple_encoder_helper_funcs);
> > +	drm_encoder_init(drm, &vga->encoder, &vga_simple_encoder_funcs,
> > +			 DRM_MODE_ENCODER_DAC);
> > +
> > +	drm_connector_helper_add(&vga->connector,
> > +				 &vga_simple_connector_helper_funcs);
> 
> I really dislike this, this is an encoder driver, not a connector driver. It
> shouldn't be responsible for creating the connector. For all it knows,
> there might not even be a VGA connector connected to the encoder, VGA
> signals could be sent to a chained encoder. That might be a bit far-fetched
> in the VGA case, but in the generic case encoder drivers shouldn't create
> connectors.

ok, so you suggest to have this split. Taking into account Rob's comment 
probably a "simple-encoder" and a "ddc-connector" for connectors using system 
i2c busses? And then connect everything via ports.


Thanks
Heiko

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

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

* [PATCH 03/11] drm: add driver for simple vga encoders
@ 2015-02-28  0:42       ` Heiko Stübner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stübner @ 2015-02-28  0:42 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Laurent,

thanks for the comments

Am Donnerstag, 26. Februar 2015, 20:33:33 schrieb Laurent Pinchart:
> On Saturday 31 January 2015 17:32:56 Heiko Stuebner wrote:
> > There exist simple vga encoders without any type of management interface
> > and just maybe a simple gpio for turning it on or off. Examples for these
> > are the Analog Devices ADV7123, Chipsea CS7123 or Micronas SDA7123.
> > 
> > Add a generic encoder driver for those that can be used by drm drivers
> > using the component framework.
> 
> The rcar-du driver already handles the adi,adv7123 compatible string used by
> this driver. A generic driver is in my opinion a very good idea, but we
> can't break the existing support. Porting the rcar-du driver to the
> component model is needed.

is this in someones plans already? Because I'm still having trouble 
understanding parts of the rockchip driver sometimes, so feel in no way 
qualified to try to get this tackled in some reasonable timeframe :-)


> > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > ---
> > 
> >  drivers/gpu/drm/i2c/Kconfig      |   6 +
> >  drivers/gpu/drm/i2c/Makefile     |   2 +
> >  drivers/gpu/drm/i2c/vga-simple.c | 325
> >  ++++++++++++++++++++++++++++++++++++
> 
> drivers/gpu/drm/i2c/ feels wrong for a platform device.

yep, i2c probably being the wrong place was one of the caveats in the cover 
letter and I was hoping some more seasoned drm people could suggest where this 
should live after all.

As your comments further down suggest that we might introduce some different 
components, simply congregate them in a "components" subdir or something 
splitting them more?


> 
> >  3 files changed, 333 insertions(+)
> >  create mode 100644 drivers/gpu/drm/i2c/vga-simple.c
> > 
> > diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
> > index 22c7ed6..319f2cb 100644
> > --- a/drivers/gpu/drm/i2c/Kconfig
> > +++ b/drivers/gpu/drm/i2c/Kconfig
> > @@ -31,4 +31,10 @@ config DRM_I2C_NXP_TDA998X
> > 
> >  	help
> >  	
> >  	  Support for NXP Semiconductors TDA998X HDMI encoders.
> > 
> > +config DRM_VGA_SIMPLE
> > +	tristate "Generic simple vga encoder"
> > +	help
> > +	  Support for vga encoder chips without any special settings
> > +	  and at most a power-control gpio.
> > +
> > 
> >  endmenu
> > 
> > diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
> > index 2c72eb5..858961f 100644
> > --- a/drivers/gpu/drm/i2c/Makefile
> > +++ b/drivers/gpu/drm/i2c/Makefile
> > @@ -10,3 +10,5 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
> > 
> >  tda998x-y := tda998x_drv.o
> >  obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
> > 
> > +
> > +obj-$(CONFIG_DRM_VGA_SIMPLE) += vga-simple.o
> > diff --git a/drivers/gpu/drm/i2c/vga-simple.c
> > b/drivers/gpu/drm/i2c/vga-simple.c new file mode 100644
> > index 0000000..bb9d19c
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i2c/vga-simple.c
> > @@ -0,0 +1,325 @@
> > +/*
> > + * 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 <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_simple(x) container_of(x, struct vga_simple,
> > connector) +#define encoder_to_vga_simple(x) container_of(x, struct
> > vga_simple, encoder) +
> > +struct vga_simple {
> > +	struct drm_connector connector;
> > +	struct drm_encoder encoder;
> > +
> > +	struct device *dev;
> > +	struct i2c_adapter *ddc;
> > +
> > +	struct regulator *vaa_reg;
> > +	struct gpio_desc *enable_gpio;
> > +
> > +	struct mutex enable_lock;
> > +	bool enabled;
> > +};
> > +
> > +/*
> > + * Connector functions
> > + */
> > +
> > +enum drm_connector_status vga_simple_detect(struct drm_connector
> > *connector, +					    bool force)
> > +{
> > +	struct vga_simple *vga = connector_to_vga_simple(connector);
> > +
> > +	if (!vga->ddc)
> > +		return connector_status_unknown;
> > +
> > +	if (drm_probe_ddc(vga->ddc))
> > +		return connector_status_connected;
> > +
> > +	return connector_status_disconnected;
> > +}
> > +
> > +void vga_simple_connector_destroy(struct drm_connector *connector)
> > +{
> > +	drm_connector_unregister(connector);
> > +	drm_connector_cleanup(connector);
> > +}
> > +
> > +struct drm_connector_funcs vga_simple_connector_funcs = {
> > +	.dpms = drm_helper_connector_dpms,
> > +	.fill_modes = drm_helper_probe_single_connector_modes,
> > +	.detect = vga_simple_detect,
> > +	.destroy = vga_simple_connector_destroy,
> > +};
> > +
> > +/*
> > + * Connector helper functions
> > + */
> > +
> > +static int vga_simple_connector_get_modes(struct drm_connector
> > *connector)
> > +{
> > +	struct vga_simple *vga = connector_to_vga_simple(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_simple_connector_mode_valid(struct drm_connector
> > *connector, +					struct drm_display_mode *mode)
> > +{
> > +	return MODE_OK;
> > +}
> > +
> > +static struct drm_encoder
> > +*vga_simple_connector_best_encoder(struct drm_connector *connector)
> > +{
> > +	struct vga_simple *vga = connector_to_vga_simple(connector);
> > +
> > +	return &vga->encoder;
> > +}
> > +
> > +static struct drm_connector_helper_funcs
> > vga_simple_connector_helper_funcs
> > = { +	.get_modes = vga_simple_connector_get_modes,
> > +	.best_encoder = vga_simple_connector_best_encoder,
> > +	.mode_valid = vga_simple_connector_mode_valid,
> > +};
> > +
> > +/*
> > + * Encoder functions
> > + */
> > +
> > +static void vga_simple_encoder_destroy(struct drm_encoder *encoder)
> > +{
> > +	drm_encoder_cleanup(encoder);
> > +}
> > +
> > +static const struct drm_encoder_funcs vga_simple_encoder_funcs = {
> > +	.destroy = vga_simple_encoder_destroy,
> > +};
> > +
> > +/*
> > + * Encoder helper functions
> > + */
> > +
> > +static void vga_simple_encoder_dpms(struct drm_encoder *encoder, int
> > mode)
> > +{
> > +	struct vga_simple *vga = encoder_to_vga_simple(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->enable_gpio)
> > +			gpiod_set_value(vga->enable_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->enable_gpio)
> > +			gpiod_set_value(vga->enable_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_simple_mode_fixup(struct drm_encoder *encoder,
> > +				  const struct drm_display_mode *mode,
> > +				  struct drm_display_mode *adjusted_mode)
> > +{
> > +	return true;
> > +}
> > +
> > +static void vga_simple_encoder_prepare(struct drm_encoder *encoder)
> > +{
> > +}
> > +
> > +static void vga_simple_encoder_mode_set(struct drm_encoder *encoder,
> > +					struct drm_display_mode *mode,
> > +					struct drm_display_mode *adjusted_mode)
> > +{
> > +}
> > +
> > +static void vga_simple_encoder_commit(struct drm_encoder *encoder)
> > +{
> > +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
> > +}
> > +
> > +static void vga_simple_encoder_disable(struct drm_encoder *encoder)
> > +{
> > +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
> > +}
> > +
> > +static const struct drm_encoder_helper_funcs
> > vga_simple_encoder_helper_funcs = {
> > +	.dpms = vga_simple_encoder_dpms,
> > +	.mode_fixup = vga_simple_mode_fixup,
> > +	.prepare = vga_simple_encoder_prepare,
> > +	.mode_set = vga_simple_encoder_mode_set,
> > +	.commit = vga_simple_encoder_commit,
> > +	.disable = vga_simple_encoder_disable,
> 
> this is interesting. Some users of this encoder (I'm thinking about rcar-du,
> for which I've just posted the patches) are already ported to atomic
> updates, while others are not. How can we support both ?

Wild guess, support both in such generic components and let bind decide which 
to use ... is there some sort of drm_is_atomic() against the master device.


> 
> > +};
> > +
> > +/*
> > + * Component helper functions
> > + */
> > +
> > +static int vga_simple_bind(struct device *dev, struct device *master,
> > +				 void *data)
> > +{
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	struct drm_device *drm = data;
> > +
> > +	struct device_node *ddc_node, *np = pdev->dev.of_node;
> > +	struct vga_simple *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->enable_gpio = devm_gpiod_get_optional(dev, "enable",
> > +						   GPIOD_OUT_LOW);
> > +	if (IS_ERR(vga->enable_gpio)) {
> > +		ret = PTR_ERR(vga->enable_gpio);
> > +		dev_err(dev, "failed to request GPIO: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	vga->enabled = false;
> > +
> > +	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");
> > +	}
> > +
> > +	vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
> > +
> > +	vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, np);
> > +	vga->encoder.of_node = np;
> > +	vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
> > +				DRM_CONNECTOR_POLL_DISCONNECT;
> > +
> > +	drm_encoder_helper_add(&vga->encoder, 
&vga_simple_encoder_helper_funcs);
> > +	drm_encoder_init(drm, &vga->encoder, &vga_simple_encoder_funcs,
> > +			 DRM_MODE_ENCODER_DAC);
> > +
> > +	drm_connector_helper_add(&vga->connector,
> > +				 &vga_simple_connector_helper_funcs);
> 
> I really dislike this, this is an encoder driver, not a connector driver. It
> shouldn't be responsible for creating the connector. For all it knows,
> there might not even be a VGA connector connected to the encoder, VGA
> signals could be sent to a chained encoder. That might be a bit far-fetched
> in the VGA case, but in the generic case encoder drivers shouldn't create
> connectors.

ok, so you suggest to have this split. Taking into account Rob's comment 
probably a "simple-encoder" and a "ddc-connector" for connectors using system 
i2c busses? And then connect everything via ports.


Thanks
Heiko

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

* Re: [PATCH 03/11] drm: add driver for simple vga encoders
  2015-02-28  0:42       ` Heiko Stübner
@ 2015-03-23 20:54         ` Heiko Stuebner
  -1 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-03-23 20:54 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, airlied-cv59FeDIM0c,
	mark.yao-TNX95d0MmH7DzftRWevZcw, mark.rutland-5wv7dgnIgG8,
	devicetree-u79uwXL29TY76Z2rM5mHXA, pawel.moll-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, galak-sgV2jX0FEOL9JmXXK+q4OQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Laurent,

Am Samstag, 28. Februar 2015, 01:42:45 schrieb Heiko Stübner:
> thanks for the comments
> 
> Am Donnerstag, 26. Februar 2015, 20:33:33 schrieb Laurent Pinchart:
> > On Saturday 31 January 2015 17:32:56 Heiko Stuebner wrote:
> > > There exist simple vga encoders without any type of management interface
> > > and just maybe a simple gpio for turning it on or off. Examples for
> > > these
> > > are the Analog Devices ADV7123, Chipsea CS7123 or Micronas SDA7123.
> > > 
> > > Add a generic encoder driver for those that can be used by drm drivers
> > > using the component framework.
> > 
> > The rcar-du driver already handles the adi,adv7123 compatible string used
> > by this driver. A generic driver is in my opinion a very good idea, but
> > we can't break the existing support. Porting the rcar-du driver to the
> > component model is needed.
> 
> is this in someones plans already? Because I'm still having trouble
> understanding parts of the rockchip driver sometimes, so feel in no way
> qualified to try to get this tackled in some reasonable timeframe :-)

currently my idea is to simply do something like the following for this :-)

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);
}


slightly similar to what some iommus do


> > > Signed-off-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
> > > ---
> > > 
> > >  drivers/gpu/drm/i2c/Kconfig      |   6 +
> > >  drivers/gpu/drm/i2c/Makefile     |   2 +
> > >  drivers/gpu/drm/i2c/vga-simple.c | 325
> > >  ++++++++++++++++++++++++++++++++++++
> > 
> > drivers/gpu/drm/i2c/ feels wrong for a platform device.
> 
> yep, i2c probably being the wrong place was one of the caveats in the cover
> letter and I was hoping some more seasoned drm people could suggest where
> this should live after all.
> 
> As your comments further down suggest that we might introduce some different
> components, simply congregate them in a "components" subdir or something
> splitting them more?

so does a "components" subdir look reasonable?


> > >  3 files changed, 333 insertions(+)
> > >  create mode 100644 drivers/gpu/drm/i2c/vga-simple.c
> > > 
> > > diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
> > > index 22c7ed6..319f2cb 100644
> > > --- a/drivers/gpu/drm/i2c/Kconfig
> > > +++ b/drivers/gpu/drm/i2c/Kconfig
> > > @@ -31,4 +31,10 @@ config DRM_I2C_NXP_TDA998X
> > > 
> > >  	help
> > >  	
> > >  	  Support for NXP Semiconductors TDA998X HDMI encoders.
> > > 
> > > +config DRM_VGA_SIMPLE
> > > +	tristate "Generic simple vga encoder"
> > > +	help
> > > +	  Support for vga encoder chips without any special settings
> > > +	  and at most a power-control gpio.
> > > +
> > > 
> > >  endmenu
> > > 
> > > diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
> > > index 2c72eb5..858961f 100644
> > > --- a/drivers/gpu/drm/i2c/Makefile
> > > +++ b/drivers/gpu/drm/i2c/Makefile
> > > @@ -10,3 +10,5 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
> > > 
> > >  tda998x-y := tda998x_drv.o
> > >  obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
> > > 
> > > +
> > > +obj-$(CONFIG_DRM_VGA_SIMPLE) += vga-simple.o
> > > diff --git a/drivers/gpu/drm/i2c/vga-simple.c
> > > b/drivers/gpu/drm/i2c/vga-simple.c new file mode 100644
> > > index 0000000..bb9d19c
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/i2c/vga-simple.c
> > > @@ -0,0 +1,325 @@
> > > +/*
> > > + * 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 <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_simple(x) container_of(x, struct vga_simple,
> > > connector) +#define encoder_to_vga_simple(x) container_of(x, struct
> > > vga_simple, encoder) +
> > > +struct vga_simple {
> > > +	struct drm_connector connector;
> > > +	struct drm_encoder encoder;
> > > +
> > > +	struct device *dev;
> > > +	struct i2c_adapter *ddc;
> > > +
> > > +	struct regulator *vaa_reg;
> > > +	struct gpio_desc *enable_gpio;
> > > +
> > > +	struct mutex enable_lock;
> > > +	bool enabled;
> > > +};
> > > +
> > > +/*
> > > + * Connector functions
> > > + */
> > > +
> > > +enum drm_connector_status vga_simple_detect(struct drm_connector
> > > *connector, +					    bool force)
> > > +{
> > > +	struct vga_simple *vga = connector_to_vga_simple(connector);
> > > +
> > > +	if (!vga->ddc)
> > > +		return connector_status_unknown;
> > > +
> > > +	if (drm_probe_ddc(vga->ddc))
> > > +		return connector_status_connected;
> > > +
> > > +	return connector_status_disconnected;
> > > +}
> > > +
> > > +void vga_simple_connector_destroy(struct drm_connector *connector)
> > > +{
> > > +	drm_connector_unregister(connector);
> > > +	drm_connector_cleanup(connector);
> > > +}
> > > +
> > > +struct drm_connector_funcs vga_simple_connector_funcs = {
> > > +	.dpms = drm_helper_connector_dpms,
> > > +	.fill_modes = drm_helper_probe_single_connector_modes,
> > > +	.detect = vga_simple_detect,
> > > +	.destroy = vga_simple_connector_destroy,
> > > +};
> > > +
> > > +/*
> > > + * Connector helper functions
> > > + */
> > > +
> > > +static int vga_simple_connector_get_modes(struct drm_connector
> > > *connector)
> > > +{
> > > +	struct vga_simple *vga = connector_to_vga_simple(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_simple_connector_mode_valid(struct drm_connector
> > > *connector, +					struct drm_display_mode *mode)
> > > +{
> > > +	return MODE_OK;
> > > +}
> > > +
> > > +static struct drm_encoder
> > > +*vga_simple_connector_best_encoder(struct drm_connector *connector)
> > > +{
> > > +	struct vga_simple *vga = connector_to_vga_simple(connector);
> > > +
> > > +	return &vga->encoder;
> > > +}
> > > +
> > > +static struct drm_connector_helper_funcs
> > > vga_simple_connector_helper_funcs
> > > = { +	.get_modes = vga_simple_connector_get_modes,
> > > +	.best_encoder = vga_simple_connector_best_encoder,
> > > +	.mode_valid = vga_simple_connector_mode_valid,
> > > +};
> > > +
> > > +/*
> > > + * Encoder functions
> > > + */
> > > +
> > > +static void vga_simple_encoder_destroy(struct drm_encoder *encoder)
> > > +{
> > > +	drm_encoder_cleanup(encoder);
> > > +}
> > > +
> > > +static const struct drm_encoder_funcs vga_simple_encoder_funcs = {
> > > +	.destroy = vga_simple_encoder_destroy,
> > > +};
> > > +
> > > +/*
> > > + * Encoder helper functions
> > > + */
> > > +
> > > +static void vga_simple_encoder_dpms(struct drm_encoder *encoder, int
> > > mode)
> > > +{
> > > +	struct vga_simple *vga = encoder_to_vga_simple(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->enable_gpio)
> > > +			gpiod_set_value(vga->enable_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->enable_gpio)
> > > +			gpiod_set_value(vga->enable_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_simple_mode_fixup(struct drm_encoder *encoder,
> > > +				  const struct drm_display_mode *mode,
> > > +				  struct drm_display_mode *adjusted_mode)
> > > +{
> > > +	return true;
> > > +}
> > > +
> > > +static void vga_simple_encoder_prepare(struct drm_encoder *encoder)
> > > +{
> > > +}
> > > +
> > > +static void vga_simple_encoder_mode_set(struct drm_encoder *encoder,
> > > +					struct drm_display_mode *mode,
> > > +					struct drm_display_mode *adjusted_mode)
> > > +{
> > > +}
> > > +
> > > +static void vga_simple_encoder_commit(struct drm_encoder *encoder)
> > > +{
> > > +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
> > > +}
> > > +
> > > +static void vga_simple_encoder_disable(struct drm_encoder *encoder)
> > > +{
> > > +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
> > > +}
> > > +
> > > +static const struct drm_encoder_helper_funcs
> > > vga_simple_encoder_helper_funcs = {
> > > +	.dpms = vga_simple_encoder_dpms,
> > > +	.mode_fixup = vga_simple_mode_fixup,
> > > +	.prepare = vga_simple_encoder_prepare,
> > > +	.mode_set = vga_simple_encoder_mode_set,
> > > +	.commit = vga_simple_encoder_commit,
> > > +	.disable = vga_simple_encoder_disable,
> > 
> > this is interesting. Some users of this encoder (I'm thinking about
> > rcar-du, for which I've just posted the patches) are already ported to
> > atomic updates, while others are not. How can we support both ?
> 
> Wild guess, support both in such generic components and let bind decide
> which to use ... is there some sort of drm_is_atomic() against the master
> device.

my hope here is that till I am finished with my part, the atomic conversion 
will have turned up "magically" :-)


> > > +};
> > > +
> > > +/*
> > > + * Component helper functions
> > > + */
> > > +
> > > +static int vga_simple_bind(struct device *dev, struct device *master,
> > > +				 void *data)
> > > +{
> > > +	struct platform_device *pdev = to_platform_device(dev);
> > > +	struct drm_device *drm = data;
> > > +
> > > +	struct device_node *ddc_node, *np = pdev->dev.of_node;
> > > +	struct vga_simple *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->enable_gpio = devm_gpiod_get_optional(dev, "enable",
> > > +						   GPIOD_OUT_LOW);
> > > +	if (IS_ERR(vga->enable_gpio)) {
> > > +		ret = PTR_ERR(vga->enable_gpio);
> > > +		dev_err(dev, "failed to request GPIO: %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	vga->enabled = false;
> > > +
> > > +	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");
> > > +	}
> > > +
> > > +	vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
> > > +
> > > +	vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, np);
> > > +	vga->encoder.of_node = np;
> > > +	vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
> > > +				DRM_CONNECTOR_POLL_DISCONNECT;
> > > +
> > > +	drm_encoder_helper_add(&vga->encoder,
> 
> &vga_simple_encoder_helper_funcs);
> 
> > > +	drm_encoder_init(drm, &vga->encoder, &vga_simple_encoder_funcs,
> > > +			 DRM_MODE_ENCODER_DAC);
> > > +
> > > +	drm_connector_helper_add(&vga->connector,
> > > +				 &vga_simple_connector_helper_funcs);
> > 
> > I really dislike this, this is an encoder driver, not a connector driver.
> > It shouldn't be responsible for creating the connector. For all it knows,
> > there might not even be a VGA connector connected to the encoder, VGA
> > signals could be sent to a chained encoder. That might be a bit
> > far-fetched in the VGA case, but in the generic case encoder drivers
> > shouldn't create connectors.
> 
> ok, so you suggest to have this split. Taking into account Rob's comment
> probably a "simple-encoder" and a "ddc-connector" for connectors using
> system i2c busses? And then connect everything via ports.

having them separate actually looks quite interesting, so definitly no argument 
from me against doing a "vga-encoder" and "vga-connector" driver using the 
already established bindings.


Heiko
--
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	[flat|nested] 38+ messages in thread

* [PATCH 03/11] drm: add driver for simple vga encoders
@ 2015-03-23 20:54         ` Heiko Stuebner
  0 siblings, 0 replies; 38+ messages in thread
From: Heiko Stuebner @ 2015-03-23 20:54 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Laurent,

Am Samstag, 28. Februar 2015, 01:42:45 schrieb Heiko St?bner:
> thanks for the comments
> 
> Am Donnerstag, 26. Februar 2015, 20:33:33 schrieb Laurent Pinchart:
> > On Saturday 31 January 2015 17:32:56 Heiko Stuebner wrote:
> > > There exist simple vga encoders without any type of management interface
> > > and just maybe a simple gpio for turning it on or off. Examples for
> > > these
> > > are the Analog Devices ADV7123, Chipsea CS7123 or Micronas SDA7123.
> > > 
> > > Add a generic encoder driver for those that can be used by drm drivers
> > > using the component framework.
> > 
> > The rcar-du driver already handles the adi,adv7123 compatible string used
> > by this driver. A generic driver is in my opinion a very good idea, but
> > we can't break the existing support. Porting the rcar-du driver to the
> > component model is needed.
> 
> is this in someones plans already? Because I'm still having trouble
> understanding parts of the rockchip driver sometimes, so feel in no way
> qualified to try to get this tackled in some reasonable timeframe :-)

currently my idea is to simply do something like the following for this :-)

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);
}


slightly similar to what some iommus do


> > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > > ---
> > > 
> > >  drivers/gpu/drm/i2c/Kconfig      |   6 +
> > >  drivers/gpu/drm/i2c/Makefile     |   2 +
> > >  drivers/gpu/drm/i2c/vga-simple.c | 325
> > >  ++++++++++++++++++++++++++++++++++++
> > 
> > drivers/gpu/drm/i2c/ feels wrong for a platform device.
> 
> yep, i2c probably being the wrong place was one of the caveats in the cover
> letter and I was hoping some more seasoned drm people could suggest where
> this should live after all.
> 
> As your comments further down suggest that we might introduce some different
> components, simply congregate them in a "components" subdir or something
> splitting them more?

so does a "components" subdir look reasonable?


> > >  3 files changed, 333 insertions(+)
> > >  create mode 100644 drivers/gpu/drm/i2c/vga-simple.c
> > > 
> > > diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
> > > index 22c7ed6..319f2cb 100644
> > > --- a/drivers/gpu/drm/i2c/Kconfig
> > > +++ b/drivers/gpu/drm/i2c/Kconfig
> > > @@ -31,4 +31,10 @@ config DRM_I2C_NXP_TDA998X
> > > 
> > >  	help
> > >  	
> > >  	  Support for NXP Semiconductors TDA998X HDMI encoders.
> > > 
> > > +config DRM_VGA_SIMPLE
> > > +	tristate "Generic simple vga encoder"
> > > +	help
> > > +	  Support for vga encoder chips without any special settings
> > > +	  and at most a power-control gpio.
> > > +
> > > 
> > >  endmenu
> > > 
> > > diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
> > > index 2c72eb5..858961f 100644
> > > --- a/drivers/gpu/drm/i2c/Makefile
> > > +++ b/drivers/gpu/drm/i2c/Makefile
> > > @@ -10,3 +10,5 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o
> > > 
> > >  tda998x-y := tda998x_drv.o
> > >  obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o
> > > 
> > > +
> > > +obj-$(CONFIG_DRM_VGA_SIMPLE) += vga-simple.o
> > > diff --git a/drivers/gpu/drm/i2c/vga-simple.c
> > > b/drivers/gpu/drm/i2c/vga-simple.c new file mode 100644
> > > index 0000000..bb9d19c
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/i2c/vga-simple.c
> > > @@ -0,0 +1,325 @@
> > > +/*
> > > + * 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 <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_simple(x) container_of(x, struct vga_simple,
> > > connector) +#define encoder_to_vga_simple(x) container_of(x, struct
> > > vga_simple, encoder) +
> > > +struct vga_simple {
> > > +	struct drm_connector connector;
> > > +	struct drm_encoder encoder;
> > > +
> > > +	struct device *dev;
> > > +	struct i2c_adapter *ddc;
> > > +
> > > +	struct regulator *vaa_reg;
> > > +	struct gpio_desc *enable_gpio;
> > > +
> > > +	struct mutex enable_lock;
> > > +	bool enabled;
> > > +};
> > > +
> > > +/*
> > > + * Connector functions
> > > + */
> > > +
> > > +enum drm_connector_status vga_simple_detect(struct drm_connector
> > > *connector, +					    bool force)
> > > +{
> > > +	struct vga_simple *vga = connector_to_vga_simple(connector);
> > > +
> > > +	if (!vga->ddc)
> > > +		return connector_status_unknown;
> > > +
> > > +	if (drm_probe_ddc(vga->ddc))
> > > +		return connector_status_connected;
> > > +
> > > +	return connector_status_disconnected;
> > > +}
> > > +
> > > +void vga_simple_connector_destroy(struct drm_connector *connector)
> > > +{
> > > +	drm_connector_unregister(connector);
> > > +	drm_connector_cleanup(connector);
> > > +}
> > > +
> > > +struct drm_connector_funcs vga_simple_connector_funcs = {
> > > +	.dpms = drm_helper_connector_dpms,
> > > +	.fill_modes = drm_helper_probe_single_connector_modes,
> > > +	.detect = vga_simple_detect,
> > > +	.destroy = vga_simple_connector_destroy,
> > > +};
> > > +
> > > +/*
> > > + * Connector helper functions
> > > + */
> > > +
> > > +static int vga_simple_connector_get_modes(struct drm_connector
> > > *connector)
> > > +{
> > > +	struct vga_simple *vga = connector_to_vga_simple(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_simple_connector_mode_valid(struct drm_connector
> > > *connector, +					struct drm_display_mode *mode)
> > > +{
> > > +	return MODE_OK;
> > > +}
> > > +
> > > +static struct drm_encoder
> > > +*vga_simple_connector_best_encoder(struct drm_connector *connector)
> > > +{
> > > +	struct vga_simple *vga = connector_to_vga_simple(connector);
> > > +
> > > +	return &vga->encoder;
> > > +}
> > > +
> > > +static struct drm_connector_helper_funcs
> > > vga_simple_connector_helper_funcs
> > > = { +	.get_modes = vga_simple_connector_get_modes,
> > > +	.best_encoder = vga_simple_connector_best_encoder,
> > > +	.mode_valid = vga_simple_connector_mode_valid,
> > > +};
> > > +
> > > +/*
> > > + * Encoder functions
> > > + */
> > > +
> > > +static void vga_simple_encoder_destroy(struct drm_encoder *encoder)
> > > +{
> > > +	drm_encoder_cleanup(encoder);
> > > +}
> > > +
> > > +static const struct drm_encoder_funcs vga_simple_encoder_funcs = {
> > > +	.destroy = vga_simple_encoder_destroy,
> > > +};
> > > +
> > > +/*
> > > + * Encoder helper functions
> > > + */
> > > +
> > > +static void vga_simple_encoder_dpms(struct drm_encoder *encoder, int
> > > mode)
> > > +{
> > > +	struct vga_simple *vga = encoder_to_vga_simple(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->enable_gpio)
> > > +			gpiod_set_value(vga->enable_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->enable_gpio)
> > > +			gpiod_set_value(vga->enable_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_simple_mode_fixup(struct drm_encoder *encoder,
> > > +				  const struct drm_display_mode *mode,
> > > +				  struct drm_display_mode *adjusted_mode)
> > > +{
> > > +	return true;
> > > +}
> > > +
> > > +static void vga_simple_encoder_prepare(struct drm_encoder *encoder)
> > > +{
> > > +}
> > > +
> > > +static void vga_simple_encoder_mode_set(struct drm_encoder *encoder,
> > > +					struct drm_display_mode *mode,
> > > +					struct drm_display_mode *adjusted_mode)
> > > +{
> > > +}
> > > +
> > > +static void vga_simple_encoder_commit(struct drm_encoder *encoder)
> > > +{
> > > +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
> > > +}
> > > +
> > > +static void vga_simple_encoder_disable(struct drm_encoder *encoder)
> > > +{
> > > +	vga_simple_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
> > > +}
> > > +
> > > +static const struct drm_encoder_helper_funcs
> > > vga_simple_encoder_helper_funcs = {
> > > +	.dpms = vga_simple_encoder_dpms,
> > > +	.mode_fixup = vga_simple_mode_fixup,
> > > +	.prepare = vga_simple_encoder_prepare,
> > > +	.mode_set = vga_simple_encoder_mode_set,
> > > +	.commit = vga_simple_encoder_commit,
> > > +	.disable = vga_simple_encoder_disable,
> > 
> > this is interesting. Some users of this encoder (I'm thinking about
> > rcar-du, for which I've just posted the patches) are already ported to
> > atomic updates, while others are not. How can we support both ?
> 
> Wild guess, support both in such generic components and let bind decide
> which to use ... is there some sort of drm_is_atomic() against the master
> device.

my hope here is that till I am finished with my part, the atomic conversion 
will have turned up "magically" :-)


> > > +};
> > > +
> > > +/*
> > > + * Component helper functions
> > > + */
> > > +
> > > +static int vga_simple_bind(struct device *dev, struct device *master,
> > > +				 void *data)
> > > +{
> > > +	struct platform_device *pdev = to_platform_device(dev);
> > > +	struct drm_device *drm = data;
> > > +
> > > +	struct device_node *ddc_node, *np = pdev->dev.of_node;
> > > +	struct vga_simple *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->enable_gpio = devm_gpiod_get_optional(dev, "enable",
> > > +						   GPIOD_OUT_LOW);
> > > +	if (IS_ERR(vga->enable_gpio)) {
> > > +		ret = PTR_ERR(vga->enable_gpio);
> > > +		dev_err(dev, "failed to request GPIO: %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	vga->enabled = false;
> > > +
> > > +	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");
> > > +	}
> > > +
> > > +	vga->vaa_reg = devm_regulator_get_optional(dev, "vaa");
> > > +
> > > +	vga->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, np);
> > > +	vga->encoder.of_node = np;
> > > +	vga->connector.polled = DRM_CONNECTOR_POLL_CONNECT |
> > > +				DRM_CONNECTOR_POLL_DISCONNECT;
> > > +
> > > +	drm_encoder_helper_add(&vga->encoder,
> 
> &vga_simple_encoder_helper_funcs);
> 
> > > +	drm_encoder_init(drm, &vga->encoder, &vga_simple_encoder_funcs,
> > > +			 DRM_MODE_ENCODER_DAC);
> > > +
> > > +	drm_connector_helper_add(&vga->connector,
> > > +				 &vga_simple_connector_helper_funcs);
> > 
> > I really dislike this, this is an encoder driver, not a connector driver.
> > It shouldn't be responsible for creating the connector. For all it knows,
> > there might not even be a VGA connector connected to the encoder, VGA
> > signals could be sent to a chained encoder. That might be a bit
> > far-fetched in the VGA case, but in the generic case encoder drivers
> > shouldn't create connectors.
> 
> ok, so you suggest to have this split. Taking into account Rob's comment
> probably a "simple-encoder" and a "ddc-connector" for connectors using
> system i2c busses? And then connect everything via ports.

having them separate actually looks quite interesting, so definitly no argument 
from me against doing a "vga-encoder" and "vga-connector" driver using the 
already established bindings.


Heiko

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

end of thread, other threads:[~2015-03-23 20:54 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-31 16:32 [PATCH 00/11] drm/rockchip: add support for lvds controller and external encoders Heiko Stuebner
2015-01-31 16:32 ` Heiko Stuebner
2015-01-31 16:32 ` [PATCH 02/11] drm: add bindings for simple vga encoders Heiko Stuebner
2015-01-31 16:32   ` Heiko Stuebner
2015-02-26 18:25   ` Laurent Pinchart
2015-02-26 18:25     ` Laurent Pinchart
2015-01-31 16:32 ` [PATCH 03/11] drm: add driver " Heiko Stuebner
2015-01-31 16:32   ` Heiko Stuebner
2015-02-26 18:33   ` Laurent Pinchart
2015-02-26 18:33     ` Laurent Pinchart
2015-02-28  0:42     ` Heiko Stübner
2015-02-28  0:42       ` Heiko Stübner
2015-03-23 20:54       ` Heiko Stuebner
2015-03-23 20:54         ` Heiko Stuebner
     [not found]   ` <1422721984-27782-4-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
2015-02-26 20:35     ` Rob Herring
2015-02-26 20:35       ` Rob Herring
2015-01-31 16:32 ` [PATCH 05/11] drm/rockchip: Add support for Rockchip Soc LVDS Heiko Stuebner
2015-01-31 16:32   ` Heiko Stuebner
2015-01-31 16:32 ` [PATCH 06/11] drm/rockchip: lvds: register a bridge when no panel is set Heiko Stuebner
2015-01-31 16:32   ` Heiko Stuebner
     [not found] ` <1422721984-27782-1-git-send-email-heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
2015-01-31 16:32   ` [PATCH 01/11] drm/encoder: allow encoders to remember their of_node Heiko Stuebner
2015-01-31 16:32     ` Heiko Stuebner
2015-01-31 16:32   ` [PATCH 04/11] dt-bindings: Add documentation for rockchip lvds Heiko Stuebner
2015-01-31 16:32     ` Heiko Stuebner
2015-02-26 18:46     ` Laurent Pinchart
2015-02-26 18:46       ` Laurent Pinchart
2015-01-31 16:33   ` [PATCH 07/11] drm/rockchip: attach rgb bridge to encoders needing it Heiko Stuebner
2015-01-31 16:33     ` Heiko Stuebner
2015-01-31 16:33   ` [PATCH 09/11] ARM: dts: rockchip: add rk3288 lcdc0 pinmux settings Heiko Stuebner
2015-01-31 16:33     ` Heiko Stuebner
2015-01-31 16:33 ` [PATCH 08/11] drm/rockchip: enable rgb ouput of vops for vga and tv connectors Heiko Stuebner
2015-01-31 16:33   ` Heiko Stuebner
2015-01-31 16:33 ` [PATCH 10/11] ARM: dts: rockchip: add rk3288 lvds node Heiko Stuebner
2015-01-31 16:33   ` Heiko Stuebner
2015-01-31 16:33 ` [PATCH 11/11] ARM: dts: rockchip: add vga encoder and enable lvds on rk3288-firefly Heiko Stuebner
2015-01-31 16:33   ` Heiko Stuebner
2015-02-26  8:52 ` [PATCH 00/11] drm/rockchip: add support for lvds controller and external encoders Heiko Stübner
2015-02-26  8:52   ` Heiko Stübner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.