2013-10-01 23:41:29

by Sean Paul

[permalink] [raw]
Subject: [PATCH 0/5] Add some missing bits for exynos5250-snow

This set adds some missing devicetree nodes to the exynos5250-snow file as well
as adds a drm_bridge driver for the ptn3460 DP-LVDS chip. This chip is used in
the exynos5250-snow board.

Sean


Sean Paul (5):
ARM: dts: Add fimd display-timings for exynos5250-snow
ARM: dts: Add dp-controller node to exynos5250-snow
drm/bridge: Add PTN3460 bridge driver
drm/exynos: Initialize ptn3460 if present
ARM: dts: Add ptn3460 to exynos5250-snow

Documentation/devicetree/bindings/drm/bridge/ptn3460.txt | 27 +++++++++++++++++++++++++
arch/arm/boot/dts/exynos5250-snow.dts | 48 ++++++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/Kconfig | 2 ++
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/bridge/Kconfig | 4 ++++
drivers/gpu/drm/bridge/Makefile | 3 +++
drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++++++++++-
include/drm/bridge/ptn3460.h | 36 +++++++++++++++++++++++++++++++++
9 files changed, 513 insertions(+), 1 deletion(-)


2013-10-01 23:41:31

by Sean Paul

[permalink] [raw]
Subject: [PATCH 2/5] ARM: dts: Add dp-controller node to exynos5250-snow

This patch adds the dp-controller node to the exynos5250-snow board dts
file.

Signed-off-by: Sean Paul <[email protected]>
---
arch/arm/boot/dts/exynos5250-snow.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index e5af3f2..780511a 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -190,6 +190,18 @@
samsung,vbus-gpio = <&gpx1 1 0>;
};

+ dp-controller {
+ samsung,color-space = <0>;
+ samsung,dynamic-range = <0>;
+ samsung,ycbcr-coeff = <0>;
+ samsung,color-depth = <1>;
+ samsung,link-rate = <0x0a>;
+ samsung,lane-count = <2>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&dp_hpd>;
+ };
+
fimd: fimd@14400000 {
display-timings {
native-mode = <&lcd_timing>;
--
1.8.4

2013-10-01 23:41:39

by Sean Paul

[permalink] [raw]
Subject: [PATCH 5/5] ARM: dts: Add ptn3460 to exynos5250-snow

This patch adds a node for the ptn3460 DP-LVDS chip in the
exynos5250-snow board dts file.

Signed-off-by: Sean Paul <[email protected]>
---
arch/arm/boot/dts/exynos5250-snow.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index 780511a..122fc1f 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -33,6 +33,13 @@
sd3_bus4: sd3-bus-width4 {
samsung,pin-drv = <0>;
};
+
+ ptn3460_gpios: ptn3460-gpios {
+ samsung,pins = "gpy2-5", "gpx1-5";
+ samsung,pin-function = <1>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
};

gpio-keys {
@@ -190,6 +197,18 @@
samsung,vbus-gpio = <&gpx1 1 0>;
};

+ i2c@12CD0000 {
+ ptn3460-bridge@20 {
+ compatible = "nxp,ptn3460";
+ reg = <0x20>;
+ powerdown-gpio = <&gpy2 5 0>;
+ reset-gpio = <&gpx1 5 0>;
+ edid-emulation = <5>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&ptn3460_gpios>;
+ };
+ };
+
dp-controller {
samsung,color-space = <0>;
samsung,dynamic-range = <0>;
--
1.8.4

2013-10-01 23:41:34

by Sean Paul

[permalink] [raw]
Subject: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
bridge chip.

Signed-off-by: Sean Paul <[email protected]>
---
.../devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/bridge/Kconfig | 4 +
drivers/gpu/drm/bridge/Makefile | 3 +
drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++
include/drm/bridge/ptn3460.h | 36 +++
7 files changed, 422 insertions(+)
create mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
create mode 100644 drivers/gpu/drm/bridge/Kconfig
create mode 100644 drivers/gpu/drm/bridge/Makefile
create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
create mode 100644 include/drm/bridge/ptn3460.h

diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
new file mode 100644
index 0000000..c1cd329
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
@@ -0,0 +1,27 @@
+ptn3460-bridge bindings
+
+Required properties:
+ - compatible: "nxp,ptn3460"
+ - reg: i2c address of the bridge
+ - powerdown-gpio: OF device-tree gpio specification
+ - reset-gpio: OF device-tree gpio specification
+ - edid-emulation: The EDID emulation entry to use
+ +-------+------------+------------------+
+ | Value | Resolution | Description |
+ | 0 | 1024x768 | NXP Generic |
+ | 1 | 1920x1080 | NXP Generic |
+ | 2 | 1920x1080 | NXP Generic |
+ | 3 | 1600x900 | Samsung LTM200KT |
+ | 4 | 1920x1080 | Samsung LTM230HT |
+ | 5 | 1366x768 | NXP Generic |
+ | 6 | 1600x900 | ChiMei M215HGE |
+ +-------+------------+------------------+
+
+Example:
+ ptn3460-bridge@20 {
+ compatible = "nxp,ptn3460";
+ reg = <0x20>;
+ powerdown-gpio = <&gpy2 5 1 0 0>;
+ reset-gpio = <&gpx1 5 1 0 0>;
+ edid-emulation = <5>;
+ };
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 955555d..cd7bfb3 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
source "drivers/gpu/drm/qxl/Kconfig"

source "drivers/gpu/drm/msm/Kconfig"
+
+source "drivers/gpu/drm/bridge/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index f089adf..9234253 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -56,3 +56,4 @@ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_MSM) += msm/
obj-y += i2c/
+obj-y += bridge/
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
new file mode 100644
index 0000000..f8db069
--- /dev/null
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -0,0 +1,4 @@
+config DRM_PTN3460
+ tristate "PTN3460 DP/LVDS bridge"
+ depends on DRM && I2C
+ ---help---
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
new file mode 100644
index 0000000..b4733e1
--- /dev/null
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -0,0 +1,3 @@
+ccflags-y := -Iinclude/drm
+
+obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
new file mode 100644
index 0000000..a9e5c1a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ptn3460.c
@@ -0,0 +1,349 @@
+/*
+ * NXP PTN3460 DP/LVDS bridge driver
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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/of.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "drmP.h"
+#include "drm_edid.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+#include "bridge/ptn3460.h"
+
+#define PTN3460_EDID_ADDR 0x0
+#define PTN3460_EDID_EMULATION_ADDR 0x84
+#define PTN3460_EDID_ENABLE_EMULATION 0
+#define PTN3460_EDID_EMULATION_SELECTION 1
+#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
+
+struct ptn3460_bridge {
+ struct drm_connector connector;
+ struct i2c_client *client;
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+ struct edid *edid;
+ int gpio_pd_n;
+ int gpio_rst_n;
+ u32 edid_emulation;
+ bool enabled;
+};
+
+static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
+ u8 *buf, int len)
+{
+ int ret;
+
+ ret = i2c_master_send(ptn_bridge->client, &addr, 1);
+ if (ret <= 0) {
+ DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = i2c_master_recv(ptn_bridge->client, buf, len);
+ if (ret <= 0) {
+ DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
+ char val)
+{
+ int ret;
+ char buf[2];
+
+ buf[0] = addr;
+ buf[1] = val;
+
+ ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
+ if (ret <= 0) {
+ DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
+{
+ int ret;
+ char val;
+
+ /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
+ ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
+ ptn_bridge->edid_emulation);
+ if (ret) {
+ DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Enable EDID emulation and select the desired EDID */
+ val = 1 << PTN3460_EDID_ENABLE_EMULATION |
+ ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
+
+ ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
+ if (ret) {
+ DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ptn3460_pre_enable(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+ int ret;
+
+ if (ptn_bridge->enabled)
+ return;
+
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_set_value(ptn_bridge->gpio_pd_n, 1);
+
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
+ gpio_set_value(ptn_bridge->gpio_rst_n, 0);
+ udelay(10);
+ gpio_set_value(ptn_bridge->gpio_rst_n, 1);
+ }
+
+ /*
+ * There's a bug in the PTN chip where it falsely asserts hotplug before
+ * it is fully functional. We're forced to wait for the maximum start up
+ * time specified in the chip's datasheet to make sure we're really up.
+ */
+ msleep(90);
+
+ ret = ptn3460_select_edid(ptn_bridge);
+ if (ret)
+ DRM_ERROR("Select edid failed ret=%d\n", ret);
+
+ ptn_bridge->enabled = true;
+}
+
+static void ptn3460_enable(struct drm_bridge *bridge)
+{
+}
+
+static void ptn3460_disable(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+
+ if (!ptn_bridge->enabled)
+ return;
+
+ ptn_bridge->enabled = false;
+
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_set_value(ptn_bridge->gpio_rst_n, 1);
+
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_set_value(ptn_bridge->gpio_pd_n, 0);
+}
+
+static void ptn3460_post_disable(struct drm_bridge *bridge)
+{
+}
+
+void ptn3460_bridge_destroy(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+
+ drm_bridge_cleanup(bridge);
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_free(ptn_bridge->gpio_pd_n);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_free(ptn_bridge->gpio_rst_n);
+ /* Nothing else to free, we've got devm allocated memory */
+}
+
+struct drm_bridge_funcs ptn3460_bridge_funcs = {
+ .pre_enable = ptn3460_pre_enable,
+ .enable = ptn3460_enable,
+ .disable = ptn3460_disable,
+ .post_disable = ptn3460_post_disable,
+ .destroy = ptn3460_bridge_destroy,
+};
+
+int ptn3460_get_modes(struct drm_connector *connector)
+{
+ struct ptn3460_bridge *ptn_bridge;
+ u8 *edid;
+ int ret, num_modes;
+ bool power_off;
+
+ ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
+
+ if (ptn_bridge->edid)
+ return drm_add_edid_modes(connector, ptn_bridge->edid);
+
+ power_off = !ptn_bridge->enabled;
+ ptn3460_pre_enable(ptn_bridge->bridge);
+
+ edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!edid) {
+ DRM_ERROR("Failed to allocate edid\n");
+ return 0;
+ }
+
+ ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
+ EDID_LENGTH);
+ if (ret) {
+ kfree(edid);
+ num_modes = 0;
+ goto out;
+ }
+
+ ptn_bridge->edid = (struct edid *)edid;
+ drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
+
+ num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
+
+out:
+ if (power_off)
+ ptn3460_disable(ptn_bridge->bridge);
+
+ return num_modes;
+}
+
+static int ptn3460_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
+{
+ struct ptn3460_bridge *ptn_bridge;
+
+ ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
+
+ return ptn_bridge->encoder;
+}
+
+struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
+ .get_modes = ptn3460_get_modes,
+ .mode_valid = ptn3460_mode_valid,
+ .best_encoder = ptn3460_best_encoder,
+};
+
+enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
+ bool force)
+{
+ return connector_status_connected;
+}
+
+void ptn3460_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_cleanup(connector);
+}
+
+struct drm_connector_funcs ptn3460_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = ptn3460_detect,
+ .destroy = ptn3460_connector_destroy,
+};
+
+int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
+ struct i2c_client *client, struct device_node *node)
+{
+ int ret;
+ struct drm_bridge *bridge;
+ struct ptn3460_bridge *ptn_bridge;
+
+ bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
+ if (!bridge) {
+ DRM_ERROR("Failed to allocate drm bridge\n");
+ return -ENOMEM;
+ }
+
+ ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
+ if (!ptn_bridge) {
+ DRM_ERROR("Failed to allocate ptn bridge\n");
+ return -ENOMEM;
+ }
+
+ ptn_bridge->client = client;
+ ptn_bridge->encoder = encoder;
+ ptn_bridge->bridge = bridge;
+ ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
+ ret = gpio_request_one(ptn_bridge->gpio_pd_n,
+ GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
+ if (ret) {
+ DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
+ /*
+ * Request the reset pin low to avoid the bridge being
+ * initialized prematurely
+ */
+ ret = gpio_request_one(ptn_bridge->gpio_rst_n,
+ GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
+ if (ret) {
+ DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
+ gpio_free(ptn_bridge->gpio_pd_n);
+ return ret;
+ }
+ }
+
+ ret = of_property_read_u32(node, "edid-emulation",
+ &ptn_bridge->edid_emulation);
+ if (ret) {
+ DRM_ERROR("Can't read edid emulation value\n");
+ goto err;
+ }
+
+ ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
+ if (ret) {
+ DRM_ERROR("Failed to initialize bridge with drm\n");
+ goto err;
+ }
+
+ bridge->driver_private = ptn_bridge;
+ encoder->bridge = bridge;
+
+ ret = drm_connector_init(dev, &ptn_bridge->connector,
+ &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ goto err;
+ }
+ drm_connector_helper_add(&ptn_bridge->connector,
+ &ptn3460_connector_helper_funcs);
+ drm_sysfs_connector_add(&ptn_bridge->connector);
+ drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
+
+ return 0;
+
+err:
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_free(ptn_bridge->gpio_pd_n);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_free(ptn_bridge->gpio_rst_n);
+ return ret;
+}
diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
new file mode 100644
index 0000000..157ffa1
--- /dev/null
+++ b/include/drm/bridge/ptn3460.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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 _DRM_BRIDGE_PTN3460_H_
+#define _DRM_BRIDGE_PTN3460_H_
+
+struct drm_device;
+struct drm_encoder;
+struct i2c_client;
+struct device_node;
+
+#ifdef CONFIG_DRM_PTN3460
+
+int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
+ struct i2c_client *client, struct device_node *node);
+#else
+
+int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
+ struct i2c_client *client, struct device_node *node)
+{
+ return 0;
+}
+
+#endif
+
+#endif
--
1.8.4

2013-10-01 23:41:37

by Sean Paul

[permalink] [raw]
Subject: [PATCH 4/5] drm/exynos: Initialize ptn3460 if present

This patch adds code to look for the ptn3460 in the device tree file on
exynos initialization. If ptn node is found, the driver will initialize
the ptn3460 driver and skip creating a DP connector (since the bridge
driver will register its own connector).

Signed-off-by: Sean Paul <[email protected]>
---
drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 1bef6dc..9cf4476 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -12,7 +12,9 @@
* option) any later version.
*/

+#include <linux/of_i2c.h>
#include <drm/drmP.h>
+#include <drm/bridge/ptn3460.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_connector.h"
@@ -20,6 +22,40 @@

static LIST_HEAD(exynos_drm_subdrv_list);

+struct bridge_init {
+ struct i2c_client *client;
+ struct device_node *node;
+};
+
+static bool find_bridge(const char *name, struct bridge_init *bridge)
+{
+ bridge->client = NULL;
+ bridge->node = of_find_node_by_name(NULL, name);
+ if (!bridge->node)
+ return false;
+
+ bridge->client = of_find_i2c_device_by_node(bridge->node);
+ if (!bridge->client)
+ return false;
+
+ return true;
+}
+
+/* returns the number of bridges attached */
+static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
+ struct drm_encoder *encoder)
+{
+ struct bridge_init bridge;
+ int ret;
+
+ if (find_bridge("ptn3460-bridge", &bridge)) {
+ ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
+ if (!ret)
+ return 1;
+ }
+ return 0;
+}
+
static int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
@@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
DRM_ERROR("failed to create encoder\n");
return -EFAULT;
}
+ subdrv->encoder = encoder;
+
+ if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
+ ret = exynos_drm_attach_lcd_bridge(dev, encoder);
+ if (ret)
+ return 0;
+ }

/*
* create and initialize a connector for this sub driver and
@@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
goto err_destroy_encoder;
}

- subdrv->encoder = encoder;
subdrv->connector = connector;

return 0;
--
1.8.4

2013-10-01 23:41:23

by Sean Paul

[permalink] [raw]
Subject: [PATCH 1/5] ARM: dts: Add fimd display-timings for exynos5250-snow

This patch adds the internal panel timings to the exynos5250-snow board
dts file.

Signed-off-by: Sean Paul <[email protected]>
---
arch/arm/boot/dts/exynos5250-snow.dts | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index e79331d..e5af3f2 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -190,6 +190,23 @@
samsung,vbus-gpio = <&gpx1 1 0>;
};

+ fimd: fimd@14400000 {
+ display-timings {
+ native-mode = <&lcd_timing>;
+ lcd_timing: 1366x768 {
+ clock-frequency = <70589280>;
+ hactive = <1366>;
+ vactive = <768>;
+ hfront-porch = <40>;
+ hback-porch = <40>;
+ hsync-len = <32>;
+ vback-porch = <10>;
+ vfront-porch = <12>;
+ vsync-len = <6>;
+ };
+ };
+ };
+
fixed-rate-clocks {
xxti {
compatible = "samsung,clock-xxti";
--
1.8.4

2013-10-02 21:07:52

by Olof Johansson

[permalink] [raw]
Subject: Re: [PATCH 0/5] Add some missing bits for exynos5250-snow

Hi,

On Tue, Oct 1, 2013 at 4:40 PM, Sean Paul <[email protected]> wrote:
> This set adds some missing devicetree nodes to the exynos5250-snow file as well
> as adds a drm_bridge driver for the ptn3460 DP-LVDS chip. This chip is used in
> the exynos5250-snow board.
>
> Sean
>
>
> Sean Paul (5):
> ARM: dts: Add fimd display-timings for exynos5250-snow
> ARM: dts: Add dp-controller node to exynos5250-snow
> drm/bridge: Add PTN3460 bridge driver
> drm/exynos: Initialize ptn3460 if present
> ARM: dts: Add ptn3460 to exynos5250-snow
>
> Documentation/devicetree/bindings/drm/bridge/ptn3460.txt | 27 +++++++++++++++++++++++++
> arch/arm/boot/dts/exynos5250-snow.dts | 48 ++++++++++++++++++++++++++++++++++++++++++++

This should go in through Kukjin and the arm-soc tree, not through DRM
(Looking at the actual patch shortly).

> drivers/gpu/drm/Kconfig | 2 ++
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/bridge/Kconfig | 4 ++++
> drivers/gpu/drm/bridge/Makefile | 3 +++
> drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Tip: When you generate the diffstat, redirect to a file or size your
window to something less than, oh, 300 characters wide or so. Maybe
even down to 80. :)


-Olof

2013-10-02 21:10:22

by Olof Johansson

[permalink] [raw]
Subject: Re: [PATCH 2/5] ARM: dts: Add dp-controller node to exynos5250-snow

On Tue, Oct 1, 2013 at 4:40 PM, Sean Paul <[email protected]> wrote:
> This patch adds the dp-controller node to the exynos5250-snow board dts
> file.
>
> Signed-off-by: Sean Paul <[email protected]>
> ---
> arch/arm/boot/dts/exynos5250-snow.dts | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
> index e5af3f2..780511a 100644
> --- a/arch/arm/boot/dts/exynos5250-snow.dts
> +++ b/arch/arm/boot/dts/exynos5250-snow.dts
> @@ -190,6 +190,18 @@
> samsung,vbus-gpio = <&gpx1 1 0>;
> };
>
> + dp-controller {
> + samsung,color-space = <0>;
> + samsung,dynamic-range = <0>;
> + samsung,ycbcr-coeff = <0>;
> + samsung,color-depth = <1>;
> + samsung,link-rate = <0x0a>;
> + samsung,lane-count = <2>;
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&dp_hpd>;
> + };

This won't actually do what I think you want. Since the dtsi has the
node name "dp-controller@145B0000", you will end up duplicating a new
node and not build on top of that one.

There's two ways to do it: Always include the unit address, or create
a label in the dtsi such that:

dp_controller: dp-controller@ {....
}

Then in your dts you can, at the root level:

&dp_controller {
... new stuff and overrides ...
};



-Olof

2013-10-02 21:20:46

by Olof Johansson

[permalink] [raw]
Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

Hi,


On Tue, Oct 1, 2013 at 4:40 PM, Sean Paul <[email protected]> wrote:
> This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
> bridge chip.
>
> Signed-off-by: Sean Paul <[email protected]>
> ---

[...]

> +Example:
> + ptn3460-bridge@20 {

Nit: Name is usually generic device name, i.e. "lvds-bridge" or
something like that.

> + compatible = "nxp,ptn3460";
> + reg = <0x20>;
> + powerdown-gpio = <&gpy2 5 1 0 0>;
> + reset-gpio = <&gpx1 5 1 0 0>;
> + edid-emulation = <5>;
> + };

[...]


> diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
> new file mode 100644
> index 0000000..157ffa1
> --- /dev/null
> +++ b/include/drm/bridge/ptn3460.h
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) 2013 Google, Inc.
> + *
> + * 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 _DRM_BRIDGE_PTN3460_H_
> +#define _DRM_BRIDGE_PTN3460_H_
> +
> +struct drm_device;
> +struct drm_encoder;
> +struct i2c_client;
> +struct device_node;
> +
> +#ifdef CONFIG_DRM_PTN3460
> +
> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
> + struct i2c_client *client, struct device_node *node);
> +#else
> +
> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
> + struct i2c_client *client, struct device_node *node)

This should be static inline int ptn3460_init(...)

> +{
> + return 0;
> +}


-0Olof

Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

Hi, thank you for your contribution and the below is my short comments,

2013/10/2 Sean Paul <[email protected]>:
> This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
> bridge chip.
>
> Signed-off-by: Sean Paul <[email protected]>
> ---
> .../devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++
> drivers/gpu/drm/Kconfig | 2 +
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/bridge/Kconfig | 4 +
> drivers/gpu/drm/bridge/Makefile | 3 +
> drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++
> include/drm/bridge/ptn3460.h | 36 +++
> 7 files changed, 422 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
> create mode 100644 drivers/gpu/drm/bridge/Kconfig
> create mode 100644 drivers/gpu/drm/bridge/Makefile
> create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
> create mode 100644 include/drm/bridge/ptn3460.h
>
> diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
> new file mode 100644
> index 0000000..c1cd329
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
> @@ -0,0 +1,27 @@
> +ptn3460-bridge bindings
> +
> +Required properties:
> + - compatible: "nxp,ptn3460"
> + - reg: i2c address of the bridge
> + - powerdown-gpio: OF device-tree gpio specification

Can a regulator be used instead of gpio in other board case?

> + - reset-gpio: OF device-tree gpio specification
> + - edid-emulation: The EDID emulation entry to use
> + +-------+------------+------------------+
> + | Value | Resolution | Description |
> + | 0 | 1024x768 | NXP Generic |
> + | 1 | 1920x1080 | NXP Generic |
> + | 2 | 1920x1080 | NXP Generic |
> + | 3 | 1600x900 | Samsung LTM200KT |
> + | 4 | 1920x1080 | Samsung LTM230HT |
> + | 5 | 1366x768 | NXP Generic |
> + | 6 | 1600x900 | ChiMei M215HGE |
> + +-------+------------+------------------+
> +
> +Example:
> + ptn3460-bridge@20 {
> + compatible = "nxp,ptn3460";
> + reg = <0x20>;
> + powerdown-gpio = <&gpy2 5 1 0 0>;
> + reset-gpio = <&gpx1 5 1 0 0>;
> + edid-emulation = <5>;
> + };
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 955555d..cd7bfb3 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
> source "drivers/gpu/drm/qxl/Kconfig"
>
> source "drivers/gpu/drm/msm/Kconfig"
> +
> +source "drivers/gpu/drm/bridge/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index f089adf..9234253 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -56,3 +56,4 @@ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
> obj-$(CONFIG_DRM_QXL) += qxl/
> obj-$(CONFIG_DRM_MSM) += msm/
> obj-y += i2c/
> +obj-y += bridge/
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> new file mode 100644
> index 0000000..f8db069
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -0,0 +1,4 @@
> +config DRM_PTN3460
> + tristate "PTN3460 DP/LVDS bridge"
> + depends on DRM && I2C
> + ---help---
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> new file mode 100644
> index 0000000..b4733e1
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -0,0 +1,3 @@
> +ccflags-y := -Iinclude/drm
> +
> +obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
> diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
> new file mode 100644
> index 0000000..a9e5c1a
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/ptn3460.c
> @@ -0,0 +1,349 @@
> +/*
> + * NXP PTN3460 DP/LVDS bridge driver
> + *
> + * Copyright (C) 2013 Google, Inc.
> + *
> + * 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/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/delay.h>
> +
> +#include "drmP.h"
> +#include "drm_edid.h"
> +#include "drm_crtc.h"
> +#include "drm_crtc_helper.h"
> +
> +#include "bridge/ptn3460.h"
> +
> +#define PTN3460_EDID_ADDR 0x0
> +#define PTN3460_EDID_EMULATION_ADDR 0x84
> +#define PTN3460_EDID_ENABLE_EMULATION 0
> +#define PTN3460_EDID_EMULATION_SELECTION 1
> +#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
> +
> +struct ptn3460_bridge {
> + struct drm_connector connector;
> + struct i2c_client *client;
> + struct drm_encoder *encoder;
> + struct drm_bridge *bridge;
> + struct edid *edid;
> + int gpio_pd_n;
> + int gpio_rst_n;
> + u32 edid_emulation;
> + bool enabled;
> +};
> +
> +static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
> + u8 *buf, int len)
> +{
> + int ret;
> +
> + ret = i2c_master_send(ptn_bridge->client, &addr, 1);
> + if (ret <= 0) {
> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
> + return ret;
> + }
> +
> + ret = i2c_master_recv(ptn_bridge->client, buf, len);
> + if (ret <= 0) {
> + DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
> + char val)
> +{
> + int ret;
> + char buf[2];
> +
> + buf[0] = addr;
> + buf[1] = val;
> +
> + ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
> + if (ret <= 0) {
> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
> +{
> + int ret;
> + char val;
> +
> + /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
> + ptn_bridge->edid_emulation);
> + if (ret) {
> + DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
> + return ret;
> + }
> +
> + /* Enable EDID emulation and select the desired EDID */
> + val = 1 << PTN3460_EDID_ENABLE_EMULATION |
> + ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
> +
> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
> + if (ret) {
> + DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void ptn3460_pre_enable(struct drm_bridge *bridge)
> +{
> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
> + int ret;
> +
> + if (ptn_bridge->enabled)
> + return;
> +
> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
> + gpio_set_value(ptn_bridge->gpio_pd_n, 1);

Ditto.

> +
> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
> + gpio_set_value(ptn_bridge->gpio_rst_n, 0);
> + udelay(10);
> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
> + }
> +
> + /*
> + * There's a bug in the PTN chip where it falsely asserts hotplug before
> + * it is fully functional. We're forced to wait for the maximum start up
> + * time specified in the chip's datasheet to make sure we're really up.
> + */
> + msleep(90);
> +
> + ret = ptn3460_select_edid(ptn_bridge);
> + if (ret)
> + DRM_ERROR("Select edid failed ret=%d\n", ret);
> +
> + ptn_bridge->enabled = true;
> +}
> +
> +static void ptn3460_enable(struct drm_bridge *bridge)
> +{
> +}
> +
> +static void ptn3460_disable(struct drm_bridge *bridge)
> +{
> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
> +
> + if (!ptn_bridge->enabled)
> + return;
> +
> + ptn_bridge->enabled = false;
> +
> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
> +
> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
> + gpio_set_value(ptn_bridge->gpio_pd_n, 0);

Ditto.

> +}
> +
> +static void ptn3460_post_disable(struct drm_bridge *bridge)
> +{
> +}
> +
> +void ptn3460_bridge_destroy(struct drm_bridge *bridge)
> +{
> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
> +
> + drm_bridge_cleanup(bridge);
> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
> + gpio_free(ptn_bridge->gpio_pd_n);

Ditto.

> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
> + gpio_free(ptn_bridge->gpio_rst_n);
> + /* Nothing else to free, we've got devm allocated memory */
> +}
> +
> +struct drm_bridge_funcs ptn3460_bridge_funcs = {
> + .pre_enable = ptn3460_pre_enable,
> + .enable = ptn3460_enable,
> + .disable = ptn3460_disable,
> + .post_disable = ptn3460_post_disable,
> + .destroy = ptn3460_bridge_destroy,
> +};
> +
> +int ptn3460_get_modes(struct drm_connector *connector)
> +{
> + struct ptn3460_bridge *ptn_bridge;
> + u8 *edid;
> + int ret, num_modes;
> + bool power_off;
> +
> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
> +
> + if (ptn_bridge->edid)
> + return drm_add_edid_modes(connector, ptn_bridge->edid);
> +
> + power_off = !ptn_bridge->enabled;
> + ptn3460_pre_enable(ptn_bridge->bridge);
> +
> + edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
> + if (!edid) {
> + DRM_ERROR("Failed to allocate edid\n");
> + return 0;
> + }
> +
> + ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
> + EDID_LENGTH);
> + if (ret) {
> + kfree(edid);
> + num_modes = 0;
> + goto out;
> + }
> +
> + ptn_bridge->edid = (struct edid *)edid;
> + drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
> +
> + num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
> +
> +out:
> + if (power_off)
> + ptn3460_disable(ptn_bridge->bridge);
> +
> + return num_modes;
> +}
> +
> +static int ptn3460_mode_valid(struct drm_connector *connector,
> + struct drm_display_mode *mode)
> +{
> + return MODE_OK;
> +}
> +
> +struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
> +{
> + struct ptn3460_bridge *ptn_bridge;
> +
> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
> +
> + return ptn_bridge->encoder;
> +}
> +
> +struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
> + .get_modes = ptn3460_get_modes,
> + .mode_valid = ptn3460_mode_valid,
> + .best_encoder = ptn3460_best_encoder,
> +};
> +
> +enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
> + bool force)
> +{
> + return connector_status_connected;
> +}
> +
> +void ptn3460_connector_destroy(struct drm_connector *connector)
> +{
> + drm_connector_cleanup(connector);
> +}
> +
> +struct drm_connector_funcs ptn3460_connector_funcs = {
> + .dpms = drm_helper_connector_dpms,
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .detect = ptn3460_detect,
> + .destroy = ptn3460_connector_destroy,
> +};

Why do you try to add a new connector here? We already have the
connector for LCD, and also provides some callbacks for it. For this,
please see exynos_drm_display_ops of exynos_drm_fimd driver, and you
can add new callbacks to there such as init callback for bridge device
initialization if needed.

> +
> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
> + struct i2c_client *client, struct device_node *node)
> +{
> + int ret;
> + struct drm_bridge *bridge;
> + struct ptn3460_bridge *ptn_bridge;
> +
> + bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
> + if (!bridge) {
> + DRM_ERROR("Failed to allocate drm bridge\n");
> + return -ENOMEM;
> + }
> +
> + ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
> + if (!ptn_bridge) {
> + DRM_ERROR("Failed to allocate ptn bridge\n");
> + return -ENOMEM;
> + }
> +
> + ptn_bridge->client = client;
> + ptn_bridge->encoder = encoder;
> + ptn_bridge->bridge = bridge;
> + ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);

Also, if a regulator is used instead?

> + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
> + ret = gpio_request_one(ptn_bridge->gpio_pd_n,
> + GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
> + if (ret) {
> + DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
> + return ret;
> + }
> + }
> +
> + ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
> + /*
> + * Request the reset pin low to avoid the bridge being
> + * initialized prematurely
> + */
> + ret = gpio_request_one(ptn_bridge->gpio_rst_n,
> + GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
> + if (ret) {
> + DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
> + gpio_free(ptn_bridge->gpio_pd_n);
> + return ret;
> + }
> + }
> +
> + ret = of_property_read_u32(node, "edid-emulation",
> + &ptn_bridge->edid_emulation);
> + if (ret) {
> + DRM_ERROR("Can't read edid emulation value\n");
> + goto err;
> + }
> +
> + ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
> + if (ret) {
> + DRM_ERROR("Failed to initialize bridge with drm\n");
> + goto err;
> + }
> +
> + bridge->driver_private = ptn_bridge;
> + encoder->bridge = bridge;
> +
> + ret = drm_connector_init(dev, &ptn_bridge->connector,
> + &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);

So it seems that here's not right place to call drm_connector_init function.

Display pipeline path could be one of,
Display Controller Display bus
---------------------------------------------------------------------------
FIMD---------------------LVDS--------------------LCD,
or
FIMD----------------------eDP---------------------LCD,
or
FIMD------------------MIPI-DSI------------------LCD,
or
FIMD-------------------------------------------------LCD

And also in case using image enhancement chip,
mDNIe-------------FIMD-LITE between Display Controller and Display
bus.

So, wouldn't the right place below FIMD driver? :)


> + if (ret) {
> + DRM_ERROR("Failed to initialize connector with drm\n");
> + goto err;
> + }
> + drm_connector_helper_add(&ptn_bridge->connector,
> + &ptn3460_connector_helper_funcs);
> + drm_sysfs_connector_add(&ptn_bridge->connector);
> + drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
> +
> + return 0;
> +
> +err:
> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
> + gpio_free(ptn_bridge->gpio_pd_n);
> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
> + gpio_free(ptn_bridge->gpio_rst_n);
> + return ret;
> +}
> diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
> new file mode 100644
> index 0000000..157ffa1
> --- /dev/null
> +++ b/include/drm/bridge/ptn3460.h
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright (C) 2013 Google, Inc.
> + *
> + * 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 _DRM_BRIDGE_PTN3460_H_
> +#define _DRM_BRIDGE_PTN3460_H_
> +
> +struct drm_device;
> +struct drm_encoder;
> +struct i2c_client;
> +struct device_node;
> +
> +#ifdef CONFIG_DRM_PTN3460
> +
> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
> + struct i2c_client *client, struct device_node *node);
> +#else
> +
> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
> + struct i2c_client *client, struct device_node *node)
> +{
> + return 0;
> +}
> +
> +#endif
> +
> +#endif
> --
> 1.8.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

Subject: Re: [PATCH 4/5] drm/exynos: Initialize ptn3460 if present

2013/10/2 Sean Paul <[email protected]>:
> This patch adds code to look for the ptn3460 in the device tree file on
> exynos initialization. If ptn node is found, the driver will initialize
> the ptn3460 driver and skip creating a DP connector (since the bridge
> driver will register its own connector).
>
> Signed-off-by: Sean Paul <[email protected]>
> ---
> drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
> 1 file changed, 43 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
> index 1bef6dc..9cf4476 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
> @@ -12,7 +12,9 @@
> * option) any later version.
> */
>
> +#include <linux/of_i2c.h>
> #include <drm/drmP.h>
> +#include <drm/bridge/ptn3460.h>
> #include "exynos_drm_drv.h"
> #include "exynos_drm_encoder.h"
> #include "exynos_drm_connector.h"
> @@ -20,6 +22,40 @@
>
> static LIST_HEAD(exynos_drm_subdrv_list);
>
> +struct bridge_init {
> + struct i2c_client *client;
> + struct device_node *node;
> +};
> +
> +static bool find_bridge(const char *name, struct bridge_init *bridge)
> +{
> + bridge->client = NULL;
> + bridge->node = of_find_node_by_name(NULL, name);

Not clear to me. Why do you try to handle device tree here, not real
device driver?. How about adding a output property to board specific
fimd dt node: i.e. output = <&ptn3460_bridge>? Actually, the output
device of fimd hw could be one of MIPI-DSI, eDP, mDNIe, LVDS bridge,
or LCD. And then, let's find ptn3460-bridge node in the fimd driver,
and initialize the ptn3460 bridge driver, and get power on or off
through exynos_drm_display_ops of the fimd driver. And all these
codes could be hided from fimd driver by moving them into
exynos_drm_display_ops. Of course, for this, you would need additional
works. So let's do it if needed.

The below is the outline of device tree I recommend,

In board dts,
i2c@I2CD000 {
ptn3460_bridge: prn3460-bridge@20 {
...
}
}

fimd@11c00000 {
...
output_dev = <&ptn3460_bridge>;
}

With this, I believe that you can do all things you want for
controlling the LVDS bridge in fimd driver.

Thanks,
Inki Dae

> + if (!bridge->node)
> + return false;
> +
> + bridge->client = of_find_i2c_device_by_node(bridge->node);
> + if (!bridge->client)
> + return false;
> +
> + return true;
> +}
> +
> +/* returns the number of bridges attached */
> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
> + struct drm_encoder *encoder)
> +{
> + struct bridge_init bridge;
> + int ret;
> +
> + if (find_bridge("ptn3460-bridge", &bridge)) {
> + ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
> + if (!ret)
> + return 1;
> + }
> + return 0;
> +}
> +
> static int exynos_drm_create_enc_conn(struct drm_device *dev,
> struct exynos_drm_subdrv *subdrv)
> {
> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
> DRM_ERROR("failed to create encoder\n");
> return -EFAULT;
> }
> + subdrv->encoder = encoder;
> +
> + if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
> + if (ret)
> + return 0;
> + }
>
> /*
> * create and initialize a connector for this sub driver and
> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
> goto err_destroy_encoder;
> }
>
> - subdrv->encoder = encoder;
> subdrv->connector = connector;
>
> return 0;
> --
> 1.8.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2013-10-03 14:58:11

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

On Thu, Oct 3, 2013 at 9:55 AM, Inki Dae <[email protected]> wrote:
> Hi, thank you for your contribution and the below is my short comments,
>
> 2013/10/2 Sean Paul <[email protected]>:
>> This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
>> bridge chip.
>>
>> Signed-off-by: Sean Paul <[email protected]>
>> ---
>> .../devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++
>> drivers/gpu/drm/Kconfig | 2 +
>> drivers/gpu/drm/Makefile | 1 +
>> drivers/gpu/drm/bridge/Kconfig | 4 +
>> drivers/gpu/drm/bridge/Makefile | 3 +
>> drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++
>> include/drm/bridge/ptn3460.h | 36 +++
>> 7 files changed, 422 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>> create mode 100644 drivers/gpu/drm/bridge/Kconfig
>> create mode 100644 drivers/gpu/drm/bridge/Makefile
>> create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
>> create mode 100644 include/drm/bridge/ptn3460.h
>>
>> diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>> new file mode 100644
>> index 0000000..c1cd329
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>> @@ -0,0 +1,27 @@
>> +ptn3460-bridge bindings
>> +
>> +Required properties:
>> + - compatible: "nxp,ptn3460"
>> + - reg: i2c address of the bridge
>> + - powerdown-gpio: OF device-tree gpio specification
>
> Can a regulator be used instead of gpio in other board case?
>

No, not to my knowledge.


>> + - reset-gpio: OF device-tree gpio specification
>> + - edid-emulation: The EDID emulation entry to use
>> + +-------+------------+------------------+
>> + | Value | Resolution | Description |
>> + | 0 | 1024x768 | NXP Generic |
>> + | 1 | 1920x1080 | NXP Generic |
>> + | 2 | 1920x1080 | NXP Generic |
>> + | 3 | 1600x900 | Samsung LTM200KT |
>> + | 4 | 1920x1080 | Samsung LTM230HT |
>> + | 5 | 1366x768 | NXP Generic |
>> + | 6 | 1600x900 | ChiMei M215HGE |
>> + +-------+------------+------------------+
>> +
>> +Example:
>> + ptn3460-bridge@20 {
>> + compatible = "nxp,ptn3460";
>> + reg = <0x20>;
>> + powerdown-gpio = <&gpy2 5 1 0 0>;
>> + reset-gpio = <&gpx1 5 1 0 0>;
>> + edid-emulation = <5>;
>> + };
>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>> index 955555d..cd7bfb3 100644
>> --- a/drivers/gpu/drm/Kconfig
>> +++ b/drivers/gpu/drm/Kconfig
>> @@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
>> source "drivers/gpu/drm/qxl/Kconfig"
>>
>> source "drivers/gpu/drm/msm/Kconfig"
>> +
>> +source "drivers/gpu/drm/bridge/Kconfig"
>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>> index f089adf..9234253 100644
>> --- a/drivers/gpu/drm/Makefile
>> +++ b/drivers/gpu/drm/Makefile
>> @@ -56,3 +56,4 @@ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
>> obj-$(CONFIG_DRM_QXL) += qxl/
>> obj-$(CONFIG_DRM_MSM) += msm/
>> obj-y += i2c/
>> +obj-y += bridge/
>> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
>> new file mode 100644
>> index 0000000..f8db069
>> --- /dev/null
>> +++ b/drivers/gpu/drm/bridge/Kconfig
>> @@ -0,0 +1,4 @@
>> +config DRM_PTN3460
>> + tristate "PTN3460 DP/LVDS bridge"
>> + depends on DRM && I2C
>> + ---help---
>> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
>> new file mode 100644
>> index 0000000..b4733e1
>> --- /dev/null
>> +++ b/drivers/gpu/drm/bridge/Makefile
>> @@ -0,0 +1,3 @@
>> +ccflags-y := -Iinclude/drm
>> +
>> +obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>> diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
>> new file mode 100644
>> index 0000000..a9e5c1a
>> --- /dev/null
>> +++ b/drivers/gpu/drm/bridge/ptn3460.c
>> @@ -0,0 +1,349 @@
>> +/*
>> + * NXP PTN3460 DP/LVDS bridge driver
>> + *
>> + * Copyright (C) 2013 Google, Inc.
>> + *
>> + * 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/of.h>
>> +#include <linux/of_gpio.h>
>> +#include <linux/i2c.h>
>> +#include <linux/gpio.h>
>> +#include <linux/delay.h>
>> +
>> +#include "drmP.h"
>> +#include "drm_edid.h"
>> +#include "drm_crtc.h"
>> +#include "drm_crtc_helper.h"
>> +
>> +#include "bridge/ptn3460.h"
>> +
>> +#define PTN3460_EDID_ADDR 0x0
>> +#define PTN3460_EDID_EMULATION_ADDR 0x84
>> +#define PTN3460_EDID_ENABLE_EMULATION 0
>> +#define PTN3460_EDID_EMULATION_SELECTION 1
>> +#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
>> +
>> +struct ptn3460_bridge {
>> + struct drm_connector connector;
>> + struct i2c_client *client;
>> + struct drm_encoder *encoder;
>> + struct drm_bridge *bridge;
>> + struct edid *edid;
>> + int gpio_pd_n;
>> + int gpio_rst_n;
>> + u32 edid_emulation;
>> + bool enabled;
>> +};
>> +
>> +static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
>> + u8 *buf, int len)
>> +{
>> + int ret;
>> +
>> + ret = i2c_master_send(ptn_bridge->client, &addr, 1);
>> + if (ret <= 0) {
>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = i2c_master_recv(ptn_bridge->client, buf, len);
>> + if (ret <= 0) {
>> + DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
>> + char val)
>> +{
>> + int ret;
>> + char buf[2];
>> +
>> + buf[0] = addr;
>> + buf[1] = val;
>> +
>> + ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
>> + if (ret <= 0) {
>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
>> +{
>> + int ret;
>> + char val;
>> +
>> + /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
>> + ptn_bridge->edid_emulation);
>> + if (ret) {
>> + DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
>> + return ret;
>> + }
>> +
>> + /* Enable EDID emulation and select the desired EDID */
>> + val = 1 << PTN3460_EDID_ENABLE_EMULATION |
>> + ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
>> +
>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
>> + if (ret) {
>> + DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void ptn3460_pre_enable(struct drm_bridge *bridge)
>> +{
>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>> + int ret;
>> +
>> + if (ptn_bridge->enabled)
>> + return;
>> +
>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>> + gpio_set_value(ptn_bridge->gpio_pd_n, 1);
>
> Ditto.
>
>> +
>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>> + gpio_set_value(ptn_bridge->gpio_rst_n, 0);
>> + udelay(10);
>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>> + }
>> +
>> + /*
>> + * There's a bug in the PTN chip where it falsely asserts hotplug before
>> + * it is fully functional. We're forced to wait for the maximum start up
>> + * time specified in the chip's datasheet to make sure we're really up.
>> + */
>> + msleep(90);
>> +
>> + ret = ptn3460_select_edid(ptn_bridge);
>> + if (ret)
>> + DRM_ERROR("Select edid failed ret=%d\n", ret);
>> +
>> + ptn_bridge->enabled = true;
>> +}
>> +
>> +static void ptn3460_enable(struct drm_bridge *bridge)
>> +{
>> +}
>> +
>> +static void ptn3460_disable(struct drm_bridge *bridge)
>> +{
>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>> +
>> + if (!ptn_bridge->enabled)
>> + return;
>> +
>> + ptn_bridge->enabled = false;
>> +
>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>> +
>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>> + gpio_set_value(ptn_bridge->gpio_pd_n, 0);
>
> Ditto.
>
>> +}
>> +
>> +static void ptn3460_post_disable(struct drm_bridge *bridge)
>> +{
>> +}
>> +
>> +void ptn3460_bridge_destroy(struct drm_bridge *bridge)
>> +{
>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>> +
>> + drm_bridge_cleanup(bridge);
>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>> + gpio_free(ptn_bridge->gpio_pd_n);
>
> Ditto.
>
>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>> + gpio_free(ptn_bridge->gpio_rst_n);
>> + /* Nothing else to free, we've got devm allocated memory */
>> +}
>> +
>> +struct drm_bridge_funcs ptn3460_bridge_funcs = {
>> + .pre_enable = ptn3460_pre_enable,
>> + .enable = ptn3460_enable,
>> + .disable = ptn3460_disable,
>> + .post_disable = ptn3460_post_disable,
>> + .destroy = ptn3460_bridge_destroy,
>> +};
>> +
>> +int ptn3460_get_modes(struct drm_connector *connector)
>> +{
>> + struct ptn3460_bridge *ptn_bridge;
>> + u8 *edid;
>> + int ret, num_modes;
>> + bool power_off;
>> +
>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>> +
>> + if (ptn_bridge->edid)
>> + return drm_add_edid_modes(connector, ptn_bridge->edid);
>> +
>> + power_off = !ptn_bridge->enabled;
>> + ptn3460_pre_enable(ptn_bridge->bridge);
>> +
>> + edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
>> + if (!edid) {
>> + DRM_ERROR("Failed to allocate edid\n");
>> + return 0;
>> + }
>> +
>> + ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
>> + EDID_LENGTH);
>> + if (ret) {
>> + kfree(edid);
>> + num_modes = 0;
>> + goto out;
>> + }
>> +
>> + ptn_bridge->edid = (struct edid *)edid;
>> + drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
>> +
>> + num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
>> +
>> +out:
>> + if (power_off)
>> + ptn3460_disable(ptn_bridge->bridge);
>> +
>> + return num_modes;
>> +}
>> +
>> +static int ptn3460_mode_valid(struct drm_connector *connector,
>> + struct drm_display_mode *mode)
>> +{
>> + return MODE_OK;
>> +}
>> +
>> +struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
>> +{
>> + struct ptn3460_bridge *ptn_bridge;
>> +
>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>> +
>> + return ptn_bridge->encoder;
>> +}
>> +
>> +struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
>> + .get_modes = ptn3460_get_modes,
>> + .mode_valid = ptn3460_mode_valid,
>> + .best_encoder = ptn3460_best_encoder,
>> +};
>> +
>> +enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
>> + bool force)
>> +{
>> + return connector_status_connected;
>> +}
>> +
>> +void ptn3460_connector_destroy(struct drm_connector *connector)
>> +{
>> + drm_connector_cleanup(connector);
>> +}
>> +
>> +struct drm_connector_funcs ptn3460_connector_funcs = {
>> + .dpms = drm_helper_connector_dpms,
>> + .fill_modes = drm_helper_probe_single_connector_modes,
>> + .detect = ptn3460_detect,
>> + .destroy = ptn3460_connector_destroy,
>> +};
>
> Why do you try to add a new connector here? We already have the
> connector for LCD, and also provides some callbacks for it. For this,
> please see exynos_drm_display_ops of exynos_drm_fimd driver, and you
> can add new callbacks to there such as init callback for bridge device
> initialization if needed.
>

We add a new connector for 2 reasons:

1) We need to override the drm detect() callback to always return true
since the DP driver will presumably return its hotplug status which
will always be low when the ptn chip is turned off.
2) We want the ability to control the result of get_modes().

I've got a patch set almost ready to tear the display ops out of fimd
and put them in the DP driver. The display ops are better suited
there, since it's the actual encoder/connector. I hope to get that
posted this week.


>> +
>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>> + struct i2c_client *client, struct device_node *node)
>> +{
>> + int ret;
>> + struct drm_bridge *bridge;
>> + struct ptn3460_bridge *ptn_bridge;
>> +
>> + bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
>> + if (!bridge) {
>> + DRM_ERROR("Failed to allocate drm bridge\n");
>> + return -ENOMEM;
>> + }
>> +
>> + ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
>> + if (!ptn_bridge) {
>> + DRM_ERROR("Failed to allocate ptn bridge\n");
>> + return -ENOMEM;
>> + }
>> +
>> + ptn_bridge->client = client;
>> + ptn_bridge->encoder = encoder;
>> + ptn_bridge->bridge = bridge;
>> + ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
>
> Also, if a regulator is used instead?
>
>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
>> + ret = gpio_request_one(ptn_bridge->gpio_pd_n,
>> + GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
>> + if (ret) {
>> + DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
>> + return ret;
>> + }
>> + }
>> +
>> + ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>> + /*
>> + * Request the reset pin low to avoid the bridge being
>> + * initialized prematurely
>> + */
>> + ret = gpio_request_one(ptn_bridge->gpio_rst_n,
>> + GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
>> + if (ret) {
>> + DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
>> + gpio_free(ptn_bridge->gpio_pd_n);
>> + return ret;
>> + }
>> + }
>> +
>> + ret = of_property_read_u32(node, "edid-emulation",
>> + &ptn_bridge->edid_emulation);
>> + if (ret) {
>> + DRM_ERROR("Can't read edid emulation value\n");
>> + goto err;
>> + }
>> +
>> + ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
>> + if (ret) {
>> + DRM_ERROR("Failed to initialize bridge with drm\n");
>> + goto err;
>> + }
>> +
>> + bridge->driver_private = ptn_bridge;
>> + encoder->bridge = bridge;
>> +
>> + ret = drm_connector_init(dev, &ptn_bridge->connector,
>> + &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
>
> So it seems that here's not right place to call drm_connector_init function.
>
> Display pipeline path could be one of,
> Display Controller Display bus
> ---------------------------------------------------------------------------
> FIMD---------------------LVDS--------------------LCD,
> or
> FIMD----------------------eDP---------------------LCD,
> or
> FIMD------------------MIPI-DSI------------------LCD,
> or
> FIMD-------------------------------------------------LCD
>
> And also in case using image enhancement chip,
> mDNIe-------------FIMD-LITE between Display Controller and Display
> bus.
>
> So, wouldn't the right place below FIMD driver? :)
>

Well, this driver should be considered outside of exynos context since
it could be used by any drm driver.

In the exynos context, the right place to implement it would be in the
dp driver, actually. However, the exynos driver has a level of
abstraction on top of the crtcs/encoders such that we need to
initialize it in the exynos_drm_core. The patchset I mentioned above
should help move things in a direction where fimd/mixer implement
drm_crtc directly and hdmi/dp implement drm_encoder/drm_connector
directly. In that world, DP would initialize the ptn driver.

>
>> + if (ret) {
>> + DRM_ERROR("Failed to initialize connector with drm\n");
>> + goto err;
>> + }
>> + drm_connector_helper_add(&ptn_bridge->connector,
>> + &ptn3460_connector_helper_funcs);
>> + drm_sysfs_connector_add(&ptn_bridge->connector);
>> + drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
>> +
>> + return 0;
>> +
>> +err:
>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>> + gpio_free(ptn_bridge->gpio_pd_n);
>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>> + gpio_free(ptn_bridge->gpio_rst_n);
>> + return ret;
>> +}
>> diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
>> new file mode 100644
>> index 0000000..157ffa1
>> --- /dev/null
>> +++ b/include/drm/bridge/ptn3460.h
>> @@ -0,0 +1,36 @@
>> +/*
>> + * Copyright (C) 2013 Google, Inc.
>> + *
>> + * 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 _DRM_BRIDGE_PTN3460_H_
>> +#define _DRM_BRIDGE_PTN3460_H_
>> +
>> +struct drm_device;
>> +struct drm_encoder;
>> +struct i2c_client;
>> +struct device_node;
>> +
>> +#ifdef CONFIG_DRM_PTN3460
>> +
>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>> + struct i2c_client *client, struct device_node *node);
>> +#else
>> +
>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>> + struct i2c_client *client, struct device_node *node)
>> +{
>> + return 0;
>> +}
>> +
>> +#endif
>> +
>> +#endif
>> --
>> 1.8.4
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> [email protected]
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2013-10-03 15:03:00

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH 4/5] drm/exynos: Initialize ptn3460 if present

On Thu, Oct 3, 2013 at 10:43 AM, Inki Dae <[email protected]> wrote:
> 2013/10/2 Sean Paul <[email protected]>:
>> This patch adds code to look for the ptn3460 in the device tree file on
>> exynos initialization. If ptn node is found, the driver will initialize
>> the ptn3460 driver and skip creating a DP connector (since the bridge
>> driver will register its own connector).
>>
>> Signed-off-by: Sean Paul <[email protected]>
>> ---
>> drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
>> 1 file changed, 43 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
>> index 1bef6dc..9cf4476 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
>> @@ -12,7 +12,9 @@
>> * option) any later version.
>> */
>>
>> +#include <linux/of_i2c.h>
>> #include <drm/drmP.h>
>> +#include <drm/bridge/ptn3460.h>
>> #include "exynos_drm_drv.h"
>> #include "exynos_drm_encoder.h"
>> #include "exynos_drm_connector.h"
>> @@ -20,6 +22,40 @@
>>
>> static LIST_HEAD(exynos_drm_subdrv_list);
>>
>> +struct bridge_init {
>> + struct i2c_client *client;
>> + struct device_node *node;
>> +};
>> +
>> +static bool find_bridge(const char *name, struct bridge_init *bridge)
>> +{
>> + bridge->client = NULL;
>> + bridge->node = of_find_node_by_name(NULL, name);
>
> Not clear to me. Why do you try to handle device tree here, not real
> device driver?. How about adding a output property to board specific
> fimd dt node: i.e. output = <&ptn3460_bridge>?

The problem doing something like this is that we won't have a handle
to drm_device if it's just a standalone driver, and so we won't be
able to register the bridge or connector. We need this init call one
way or another.


> Actually, the output
> device of fimd hw could be one of MIPI-DSI, eDP, mDNIe, LVDS bridge,
> or LCD. And then, let's find ptn3460-bridge node in the fimd driver,
> and initialize the ptn3460 bridge driver, and get power on or off
> through exynos_drm_display_ops of the fimd driver. And all these
> codes could be hided from fimd driver by moving them into
> exynos_drm_display_ops. Of course, for this, you would need additional
> works. So let's do it if needed.
>
> The below is the outline of device tree I recommend,
>
> In board dts,
> i2c@I2CD000 {
> ptn3460_bridge: prn3460-bridge@20 {
> ...
> }
> }
>
> fimd@11c00000 {
> ...
> output_dev = <&ptn3460_bridge>;
> }
>
> With this, I believe that you can do all things you want for
> controlling the LVDS bridge in fimd driver.
>

No, this isn't what I want to do. The bridge should not hang off fimd
since it's a crtc. The bridge should only be initialized by the DP
driver (it doesn't make sense to initialize ptn when you're using
MIPI/LVDS/whatever).

Since the actual crtc/encoder drivers are abstracted through
exynos_drm_core/crtc/encoder, we need to initialize the ptn driver in
the abstraction layers in order to hook it directly into drm.

Sean


> Thanks,
> Inki Dae
>
>> + if (!bridge->node)
>> + return false;
>> +
>> + bridge->client = of_find_i2c_device_by_node(bridge->node);
>> + if (!bridge->client)
>> + return false;
>> +
>> + return true;
>> +}
>> +
>> +/* returns the number of bridges attached */
>> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>> + struct drm_encoder *encoder)
>> +{
>> + struct bridge_init bridge;
>> + int ret;
>> +
>> + if (find_bridge("ptn3460-bridge", &bridge)) {
>> + ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
>> + if (!ret)
>> + return 1;
>> + }
>> + return 0;
>> +}
>> +
>> static int exynos_drm_create_enc_conn(struct drm_device *dev,
>> struct exynos_drm_subdrv *subdrv)
>> {
>> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>> DRM_ERROR("failed to create encoder\n");
>> return -EFAULT;
>> }
>> + subdrv->encoder = encoder;
>> +
>> + if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
>> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
>> + if (ret)
>> + return 0;
>> + }
>>
>> /*
>> * create and initialize a connector for this sub driver and
>> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>> goto err_destroy_encoder;
>> }
>>
>> - subdrv->encoder = encoder;
>> subdrv->connector = connector;
>>
>> return 0;
>> --
>> 1.8.4
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> [email protected]
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2013-10-03 16:07:25

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH 2/5] ARM: dts: Add dp-controller node to exynos5250-snow

On Wed, Oct 2, 2013 at 5:10 PM, Olof Johansson <[email protected]> wrote:
> On Tue, Oct 1, 2013 at 4:40 PM, Sean Paul <[email protected]> wrote:
>> This patch adds the dp-controller node to the exynos5250-snow board dts
>> file.
>>
>> Signed-off-by: Sean Paul <[email protected]>
>> ---
>> arch/arm/boot/dts/exynos5250-snow.dts | 12 ++++++++++++
>> 1 file changed, 12 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
>> index e5af3f2..780511a 100644
>> --- a/arch/arm/boot/dts/exynos5250-snow.dts
>> +++ b/arch/arm/boot/dts/exynos5250-snow.dts
>> @@ -190,6 +190,18 @@
>> samsung,vbus-gpio = <&gpx1 1 0>;
>> };
>>
>> + dp-controller {
>> + samsung,color-space = <0>;
>> + samsung,dynamic-range = <0>;
>> + samsung,ycbcr-coeff = <0>;
>> + samsung,color-depth = <1>;
>> + samsung,link-rate = <0x0a>;
>> + samsung,lane-count = <2>;
>> +
>> + pinctrl-names = "default";
>> + pinctrl-0 = <&dp_hpd>;
>> + };
>
> This won't actually do what I think you want. Since the dtsi has the
> node name "dp-controller@145B0000", you will end up duplicating a new
> node and not build on top of that one.
>

Thanks, Olof. I based this off Inki's exynos-drm-next tree which does
not have the latest from arm-soc, I'll re-upload.

Sean


> There's two ways to do it: Always include the unit address, or create
> a label in the dtsi such that:
>
> dp_controller: dp-controller@ {....
> }
>
> Then in your dts you can, at the root level:
>
> &dp_controller {
> ... new stuff and overrides ...
> };
>
>
>
> -Olof

Subject: Re: [PATCH 4/5] drm/exynos: Initialize ptn3460 if present

2013/10/4 Sean Paul <[email protected]>:
> On Thu, Oct 3, 2013 at 10:43 AM, Inki Dae <[email protected]> wrote:
>> 2013/10/2 Sean Paul <[email protected]>:
>>> This patch adds code to look for the ptn3460 in the device tree file on
>>> exynos initialization. If ptn node is found, the driver will initialize
>>> the ptn3460 driver and skip creating a DP connector (since the bridge
>>> driver will register its own connector).
>>>
>>> Signed-off-by: Sean Paul <[email protected]>
>>> ---
>>> drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
>>> 1 file changed, 43 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
>>> index 1bef6dc..9cf4476 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
>>> @@ -12,7 +12,9 @@
>>> * option) any later version.
>>> */
>>>
>>> +#include <linux/of_i2c.h>
>>> #include <drm/drmP.h>
>>> +#include <drm/bridge/ptn3460.h>
>>> #include "exynos_drm_drv.h"
>>> #include "exynos_drm_encoder.h"
>>> #include "exynos_drm_connector.h"
>>> @@ -20,6 +22,40 @@
>>>
>>> static LIST_HEAD(exynos_drm_subdrv_list);
>>>
>>> +struct bridge_init {
>>> + struct i2c_client *client;
>>> + struct device_node *node;
>>> +};
>>> +
>>> +static bool find_bridge(const char *name, struct bridge_init *bridge)
>>> +{
>>> + bridge->client = NULL;
>>> + bridge->node = of_find_node_by_name(NULL, name);
>>
>> Not clear to me. Why do you try to handle device tree here, not real
>> device driver?. How about adding a output property to board specific
>> fimd dt node: i.e. output = <&ptn3460_bridge>?
>
> The problem doing something like this is that we won't have a handle
> to drm_device if it's just a standalone driver, and so we won't be
> able to register the bridge or connector. We need this init call one
> way or another.
>

At least, dt binding shoul be done in real device driver so this way
is not good. Let's find a better way.

>
>> Actually, the output
>> device of fimd hw could be one of MIPI-DSI, eDP, mDNIe, LVDS bridge,
>> or LCD. And then, let's find ptn3460-bridge node in the fimd driver,
>> and initialize the ptn3460 bridge driver, and get power on or off
>> through exynos_drm_display_ops of the fimd driver. And all these
>> codes could be hided from fimd driver by moving them into
>> exynos_drm_display_ops. Of course, for this, you would need additional
>> works. So let's do it if needed.
>>
>> The below is the outline of device tree I recommend,
>>
>> In board dts,
>> i2c@I2CD000 {
>> ptn3460_bridge: prn3460-bridge@20 {
>> ...
>> }
>> }
>>
>> fimd@11c00000 {
>> ...
>> output_dev = <&ptn3460_bridge>;
>> }
>>
>> With this, I believe that you can do all things you want for
>> controlling the LVDS bridge in fimd driver.
>>
>
> No, this isn't what I want to do. The bridge should not hang off fimd
> since it's a crtc. The bridge should only be initialized by the DP
> driver (it doesn't make sense to initialize ptn when you're using
> MIPI/LVDS/whatever).

I don't mean that the bridge device should be initialized by fimd
directly but the fimd driver provides just interfaces abstracted to
control the bridge device. And basically, the exynos_drm_display_ops
shouldn't be in fimd driver but in real connector driver; i.e. lcd
panel or LVDS driver. The reason I placed the exynos_drm_display_ops
in fimd driver is that lcd panel driver is controlled by lcd class
depended on Linux framebuffer, and I thought the panel driver should
be shared with drm driver in case of ARM SoC. The
exynos_drm_display_ops should be moved into right place if something
better exists some time or other.

So how can the DP driver control the bridge device as of now? the DP
you mentioned would be eDP, and the eDP driver is placed in
drivers/video/exynos/, and also MIPI-DSI driver.

>
> Since the actual crtc/encoder drivers are abstracted through
> exynos_drm_core/crtc/encoder, we need to initialize the ptn driver in
> the abstraction layers in order to hook it directly into drm.
>
> Sean
>
>
>> Thanks,
>> Inki Dae
>>
>>> + if (!bridge->node)
>>> + return false;
>>> +
>>> + bridge->client = of_find_i2c_device_by_node(bridge->node);
>>> + if (!bridge->client)
>>> + return false;
>>> +
>>> + return true;
>>> +}
>>> +
>>> +/* returns the number of bridges attached */
>>> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>>> + struct drm_encoder *encoder)
>>> +{
>>> + struct bridge_init bridge;
>>> + int ret;
>>> +
>>> + if (find_bridge("ptn3460-bridge", &bridge)) {
>>> + ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
>>> + if (!ret)
>>> + return 1;
>>> + }
>>> + return 0;
>>> +}
>>> +
>>> static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>> struct exynos_drm_subdrv *subdrv)
>>> {
>>> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>> DRM_ERROR("failed to create encoder\n");
>>> return -EFAULT;
>>> }
>>> + subdrv->encoder = encoder;
>>> +
>>> + if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
>>> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
>>> + if (ret)
>>> + return 0;
>>> + }
>>>
>>> /*
>>> * create and initialize a connector for this sub driver and
>>> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>> goto err_destroy_encoder;
>>> }
>>>
>>> - subdrv->encoder = encoder;
>>> subdrv->connector = connector;
>>>
>>> return 0;
>>> --
>>> 1.8.4
>>>
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> [email protected]
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> _______________________________________________
> dri-devel mailing list
> [email protected]
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

2013-10-03 17:28:16

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH 4/5] drm/exynos: Initialize ptn3460 if present

On Thu, Oct 3, 2013 at 1:18 PM, Inki Dae <[email protected]> wrote:
> 2013/10/4 Sean Paul <[email protected]>:
>> On Thu, Oct 3, 2013 at 10:43 AM, Inki Dae <[email protected]> wrote:
>>> 2013/10/2 Sean Paul <[email protected]>:
>>>> This patch adds code to look for the ptn3460 in the device tree file on
>>>> exynos initialization. If ptn node is found, the driver will initialize
>>>> the ptn3460 driver and skip creating a DP connector (since the bridge
>>>> driver will register its own connector).
>>>>
>>>> Signed-off-by: Sean Paul <[email protected]>
>>>> ---
>>>> drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
>>>> 1 file changed, 43 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
>>>> index 1bef6dc..9cf4476 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
>>>> @@ -12,7 +12,9 @@
>>>> * option) any later version.
>>>> */
>>>>
>>>> +#include <linux/of_i2c.h>
>>>> #include <drm/drmP.h>
>>>> +#include <drm/bridge/ptn3460.h>
>>>> #include "exynos_drm_drv.h"
>>>> #include "exynos_drm_encoder.h"
>>>> #include "exynos_drm_connector.h"
>>>> @@ -20,6 +22,40 @@
>>>>
>>>> static LIST_HEAD(exynos_drm_subdrv_list);
>>>>
>>>> +struct bridge_init {
>>>> + struct i2c_client *client;
>>>> + struct device_node *node;
>>>> +};
>>>> +
>>>> +static bool find_bridge(const char *name, struct bridge_init *bridge)
>>>> +{
>>>> + bridge->client = NULL;
>>>> + bridge->node = of_find_node_by_name(NULL, name);
>>>
>>> Not clear to me. Why do you try to handle device tree here, not real
>>> device driver?. How about adding a output property to board specific
>>> fimd dt node: i.e. output = <&ptn3460_bridge>?
>>
>> The problem doing something like this is that we won't have a handle
>> to drm_device if it's just a standalone driver, and so we won't be
>> able to register the bridge or connector. We need this init call one
>> way or another.
>>
>
> At least, dt binding shoul be done in real device driver so this way
> is not good. Let's find a better way.
>

Right, so this is kind of tricky. If you do it in a "real" device
driver, you end up parsing the dt stuff in the probe, and then racing
the init callback. I figured it would be best just to do everything in
one place without races.

Hopefully I'm just missing a good way to solve this problem, any concrete ideas?

>>
>>> Actually, the output
>>> device of fimd hw could be one of MIPI-DSI, eDP, mDNIe, LVDS bridge,
>>> or LCD. And then, let's find ptn3460-bridge node in the fimd driver,
>>> and initialize the ptn3460 bridge driver, and get power on or off
>>> through exynos_drm_display_ops of the fimd driver. And all these
>>> codes could be hided from fimd driver by moving them into
>>> exynos_drm_display_ops. Of course, for this, you would need additional
>>> works. So let's do it if needed.
>>>
>>> The below is the outline of device tree I recommend,
>>>
>>> In board dts,
>>> i2c@I2CD000 {
>>> ptn3460_bridge: prn3460-bridge@20 {
>>> ...
>>> }
>>> }
>>>
>>> fimd@11c00000 {
>>> ...
>>> output_dev = <&ptn3460_bridge>;
>>> }
>>>
>>> With this, I believe that you can do all things you want for
>>> controlling the LVDS bridge in fimd driver.
>>>
>>
>> No, this isn't what I want to do. The bridge should not hang off fimd
>> since it's a crtc. The bridge should only be initialized by the DP
>> driver (it doesn't make sense to initialize ptn when you're using
>> MIPI/LVDS/whatever).
>
> I don't mean that the bridge device should be initialized by fimd
> directly but the fimd driver provides just interfaces abstracted to
> control the bridge device. And basically, the exynos_drm_display_ops
> shouldn't be in fimd driver but in real connector driver; i.e. lcd
> panel or LVDS driver. The reason I placed the exynos_drm_display_ops
> in fimd driver is that lcd panel driver is controlled by lcd class
> depended on Linux framebuffer, and I thought the panel driver should
> be shared with drm driver in case of ARM SoC. The
> exynos_drm_display_ops should be moved into right place if something
> better exists some time or other.
>
> So how can the DP driver control the bridge device as of now? the DP
> you mentioned would be eDP, and the eDP driver is placed in
> drivers/video/exynos/, and also MIPI-DSI driver.
>

It can't. The DP driver just operates on its own and display either
comes up or it doesn't depending on whether fimd happens to be
initialized first. As I mentioned earlier, I have a patch set which
moves DP driver into drm/exynos and removes the display_ops from fimd.
That will fix this issue.

Sean


>>
>> Since the actual crtc/encoder drivers are abstracted through
>> exynos_drm_core/crtc/encoder, we need to initialize the ptn driver in
>> the abstraction layers in order to hook it directly into drm.
>>
>> Sean
>>
>>
>>> Thanks,
>>> Inki Dae
>>>
>>>> + if (!bridge->node)
>>>> + return false;
>>>> +
>>>> + bridge->client = of_find_i2c_device_by_node(bridge->node);
>>>> + if (!bridge->client)
>>>> + return false;
>>>> +
>>>> + return true;
>>>> +}
>>>> +
>>>> +/* returns the number of bridges attached */
>>>> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>>>> + struct drm_encoder *encoder)
>>>> +{
>>>> + struct bridge_init bridge;
>>>> + int ret;
>>>> +
>>>> + if (find_bridge("ptn3460-bridge", &bridge)) {
>>>> + ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
>>>> + if (!ret)
>>>> + return 1;
>>>> + }
>>>> + return 0;
>>>> +}
>>>> +
>>>> static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>>> struct exynos_drm_subdrv *subdrv)
>>>> {
>>>> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>>> DRM_ERROR("failed to create encoder\n");
>>>> return -EFAULT;
>>>> }
>>>> + subdrv->encoder = encoder;
>>>> +
>>>> + if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
>>>> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
>>>> + if (ret)
>>>> + return 0;
>>>> + }
>>>>
>>>> /*
>>>> * create and initialize a connector for this sub driver and
>>>> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>>> goto err_destroy_encoder;
>>>> }
>>>>
>>>> - subdrv->encoder = encoder;
>>>> subdrv->connector = connector;
>>>>
>>>> return 0;
>>>> --
>>>> 1.8.4
>>>>
>>>>
>>>> _______________________________________________
>>>> linux-arm-kernel mailing list
>>>> [email protected]
>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>> _______________________________________________
>> dri-devel mailing list
>> [email protected]
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

2013/10/3 Sean Paul <[email protected]>:
> On Thu, Oct 3, 2013 at 9:55 AM, Inki Dae <[email protected]> wrote:
>> Hi, thank you for your contribution and the below is my short comments,
>>
>> 2013/10/2 Sean Paul <[email protected]>:
>>> This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
>>> bridge chip.
>>>
>>> Signed-off-by: Sean Paul <[email protected]>
>>> ---
>>> .../devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++
>>> drivers/gpu/drm/Kconfig | 2 +
>>> drivers/gpu/drm/Makefile | 1 +
>>> drivers/gpu/drm/bridge/Kconfig | 4 +
>>> drivers/gpu/drm/bridge/Makefile | 3 +
>>> drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++
>>> include/drm/bridge/ptn3460.h | 36 +++
>>> 7 files changed, 422 insertions(+)
>>> create mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>> create mode 100644 drivers/gpu/drm/bridge/Kconfig
>>> create mode 100644 drivers/gpu/drm/bridge/Makefile
>>> create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
>>> create mode 100644 include/drm/bridge/ptn3460.h
>>>
>>> diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>> new file mode 100644
>>> index 0000000..c1cd329
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>> @@ -0,0 +1,27 @@
>>> +ptn3460-bridge bindings
>>> +
>>> +Required properties:
>>> + - compatible: "nxp,ptn3460"
>>> + - reg: i2c address of the bridge
>>> + - powerdown-gpio: OF device-tree gpio specification
>>
>> Can a regulator be used instead of gpio in other board case?
>>
>
> No, not to my knowledge.
>

Hm.. plz check it out again. the gpio pin is specific to board, and
the the gpio be used as power source trigger could be replaced with a
regulator according to board design. So you should consider all
possibilities even though there are no other cases yet: other board
could use a regulator instead.

>
>>> + - reset-gpio: OF device-tree gpio specification
>>> + - edid-emulation: The EDID emulation entry to use
>>> + +-------+------------+------------------+
>>> + | Value | Resolution | Description |
>>> + | 0 | 1024x768 | NXP Generic |
>>> + | 1 | 1920x1080 | NXP Generic |
>>> + | 2 | 1920x1080 | NXP Generic |
>>> + | 3 | 1600x900 | Samsung LTM200KT |
>>> + | 4 | 1920x1080 | Samsung LTM230HT |
>>> + | 5 | 1366x768 | NXP Generic |
>>> + | 6 | 1600x900 | ChiMei M215HGE |
>>> + +-------+------------+------------------+
>>> +
>>> +Example:
>>> + ptn3460-bridge@20 {
>>> + compatible = "nxp,ptn3460";
>>> + reg = <0x20>;
>>> + powerdown-gpio = <&gpy2 5 1 0 0>;
>>> + reset-gpio = <&gpx1 5 1 0 0>;
>>> + edid-emulation = <5>;
>>> + };
>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>> index 955555d..cd7bfb3 100644
>>> --- a/drivers/gpu/drm/Kconfig
>>> +++ b/drivers/gpu/drm/Kconfig
>>> @@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
>>> source "drivers/gpu/drm/qxl/Kconfig"
>>>
>>> source "drivers/gpu/drm/msm/Kconfig"
>>> +
>>> +source "drivers/gpu/drm/bridge/Kconfig"
>>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>>> index f089adf..9234253 100644
>>> --- a/drivers/gpu/drm/Makefile
>>> +++ b/drivers/gpu/drm/Makefile
>>> @@ -56,3 +56,4 @@ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
>>> obj-$(CONFIG_DRM_QXL) += qxl/
>>> obj-$(CONFIG_DRM_MSM) += msm/
>>> obj-y += i2c/
>>> +obj-y += bridge/
>>> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
>>> new file mode 100644
>>> index 0000000..f8db069
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/bridge/Kconfig
>>> @@ -0,0 +1,4 @@
>>> +config DRM_PTN3460
>>> + tristate "PTN3460 DP/LVDS bridge"
>>> + depends on DRM && I2C
>>> + ---help---
>>> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
>>> new file mode 100644
>>> index 0000000..b4733e1
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/bridge/Makefile
>>> @@ -0,0 +1,3 @@
>>> +ccflags-y := -Iinclude/drm
>>> +
>>> +obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>>> diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
>>> new file mode 100644
>>> index 0000000..a9e5c1a
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/bridge/ptn3460.c
>>> @@ -0,0 +1,349 @@
>>> +/*
>>> + * NXP PTN3460 DP/LVDS bridge driver
>>> + *
>>> + * Copyright (C) 2013 Google, Inc.
>>> + *
>>> + * 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/of.h>
>>> +#include <linux/of_gpio.h>
>>> +#include <linux/i2c.h>
>>> +#include <linux/gpio.h>
>>> +#include <linux/delay.h>
>>> +
>>> +#include "drmP.h"
>>> +#include "drm_edid.h"
>>> +#include "drm_crtc.h"
>>> +#include "drm_crtc_helper.h"
>>> +
>>> +#include "bridge/ptn3460.h"
>>> +
>>> +#define PTN3460_EDID_ADDR 0x0
>>> +#define PTN3460_EDID_EMULATION_ADDR 0x84
>>> +#define PTN3460_EDID_ENABLE_EMULATION 0
>>> +#define PTN3460_EDID_EMULATION_SELECTION 1
>>> +#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
>>> +
>>> +struct ptn3460_bridge {
>>> + struct drm_connector connector;
>>> + struct i2c_client *client;
>>> + struct drm_encoder *encoder;
>>> + struct drm_bridge *bridge;
>>> + struct edid *edid;
>>> + int gpio_pd_n;
>>> + int gpio_rst_n;
>>> + u32 edid_emulation;
>>> + bool enabled;
>>> +};
>>> +
>>> +static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
>>> + u8 *buf, int len)
>>> +{
>>> + int ret;
>>> +
>>> + ret = i2c_master_send(ptn_bridge->client, &addr, 1);
>>> + if (ret <= 0) {
>>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + ret = i2c_master_recv(ptn_bridge->client, buf, len);
>>> + if (ret <= 0) {
>>> + DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
>>> + char val)
>>> +{
>>> + int ret;
>>> + char buf[2];
>>> +
>>> + buf[0] = addr;
>>> + buf[1] = val;
>>> +
>>> + ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
>>> + if (ret <= 0) {
>>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
>>> +{
>>> + int ret;
>>> + char val;
>>> +
>>> + /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
>>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
>>> + ptn_bridge->edid_emulation);
>>> + if (ret) {
>>> + DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + /* Enable EDID emulation and select the desired EDID */
>>> + val = 1 << PTN3460_EDID_ENABLE_EMULATION |
>>> + ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
>>> +
>>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
>>> + if (ret) {
>>> + DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void ptn3460_pre_enable(struct drm_bridge *bridge)
>>> +{
>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>> + int ret;
>>> +
>>> + if (ptn_bridge->enabled)
>>> + return;
>>> +
>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>> + gpio_set_value(ptn_bridge->gpio_pd_n, 1);
>>
>> Ditto.
>>
>>> +
>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 0);
>>> + udelay(10);
>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>> + }
>>> +
>>> + /*
>>> + * There's a bug in the PTN chip where it falsely asserts hotplug before
>>> + * it is fully functional. We're forced to wait for the maximum start up
>>> + * time specified in the chip's datasheet to make sure we're really up.
>>> + */
>>> + msleep(90);
>>> +
>>> + ret = ptn3460_select_edid(ptn_bridge);
>>> + if (ret)
>>> + DRM_ERROR("Select edid failed ret=%d\n", ret);
>>> +
>>> + ptn_bridge->enabled = true;
>>> +}
>>> +
>>> +static void ptn3460_enable(struct drm_bridge *bridge)
>>> +{
>>> +}
>>> +
>>> +static void ptn3460_disable(struct drm_bridge *bridge)
>>> +{
>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>> +
>>> + if (!ptn_bridge->enabled)
>>> + return;
>>> +
>>> + ptn_bridge->enabled = false;
>>> +
>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>> +
>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>> + gpio_set_value(ptn_bridge->gpio_pd_n, 0);
>>
>> Ditto.
>>
>>> +}
>>> +
>>> +static void ptn3460_post_disable(struct drm_bridge *bridge)
>>> +{
>>> +}
>>> +
>>> +void ptn3460_bridge_destroy(struct drm_bridge *bridge)
>>> +{
>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>> +
>>> + drm_bridge_cleanup(bridge);
>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>
>> Ditto.
>>
>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>> + gpio_free(ptn_bridge->gpio_rst_n);
>>> + /* Nothing else to free, we've got devm allocated memory */
>>> +}
>>> +
>>> +struct drm_bridge_funcs ptn3460_bridge_funcs = {
>>> + .pre_enable = ptn3460_pre_enable,
>>> + .enable = ptn3460_enable,
>>> + .disable = ptn3460_disable,
>>> + .post_disable = ptn3460_post_disable,
>>> + .destroy = ptn3460_bridge_destroy,
>>> +};
>>> +
>>> +int ptn3460_get_modes(struct drm_connector *connector)
>>> +{
>>> + struct ptn3460_bridge *ptn_bridge;
>>> + u8 *edid;
>>> + int ret, num_modes;
>>> + bool power_off;
>>> +
>>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>>> +
>>> + if (ptn_bridge->edid)
>>> + return drm_add_edid_modes(connector, ptn_bridge->edid);
>>> +
>>> + power_off = !ptn_bridge->enabled;
>>> + ptn3460_pre_enable(ptn_bridge->bridge);
>>> +
>>> + edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
>>> + if (!edid) {
>>> + DRM_ERROR("Failed to allocate edid\n");
>>> + return 0;
>>> + }
>>> +
>>> + ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
>>> + EDID_LENGTH);
>>> + if (ret) {
>>> + kfree(edid);
>>> + num_modes = 0;
>>> + goto out;
>>> + }
>>> +
>>> + ptn_bridge->edid = (struct edid *)edid;
>>> + drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
>>> +
>>> + num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
>>> +
>>> +out:
>>> + if (power_off)
>>> + ptn3460_disable(ptn_bridge->bridge);
>>> +
>>> + return num_modes;
>>> +}
>>> +
>>> +static int ptn3460_mode_valid(struct drm_connector *connector,
>>> + struct drm_display_mode *mode)
>>> +{
>>> + return MODE_OK;
>>> +}
>>> +
>>> +struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
>>> +{
>>> + struct ptn3460_bridge *ptn_bridge;
>>> +
>>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>>> +
>>> + return ptn_bridge->encoder;
>>> +}
>>> +
>>> +struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
>>> + .get_modes = ptn3460_get_modes,
>>> + .mode_valid = ptn3460_mode_valid,
>>> + .best_encoder = ptn3460_best_encoder,
>>> +};
>>> +
>>> +enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
>>> + bool force)
>>> +{
>>> + return connector_status_connected;
>>> +}
>>> +
>>> +void ptn3460_connector_destroy(struct drm_connector *connector)
>>> +{
>>> + drm_connector_cleanup(connector);
>>> +}
>>> +
>>> +struct drm_connector_funcs ptn3460_connector_funcs = {
>>> + .dpms = drm_helper_connector_dpms,
>>> + .fill_modes = drm_helper_probe_single_connector_modes,
>>> + .detect = ptn3460_detect,
>>> + .destroy = ptn3460_connector_destroy,
>>> +};
>>
>> Why do you try to add a new connector here? We already have the
>> connector for LCD, and also provides some callbacks for it. For this,
>> please see exynos_drm_display_ops of exynos_drm_fimd driver, and you
>> can add new callbacks to there such as init callback for bridge device
>> initialization if needed.
>>
>
> We add a new connector for 2 reasons:
>
> 1) We need to override the drm detect() callback to always return true
> since the DP driver will presumably return its hotplug status which
> will always be low when the ptn chip is turned off.
> 2) We want the ability to control the result of get_modes().
>
> I've got a patch set almost ready to tear the display ops out of fimd
> and put them in the DP driver.

Really? if so, that is ideal something we want and we should go. But
isn't the DP driver placed in drivers/video/exynos? How did you take
care of that? Actually, for this, we planned to use CDF(Common Display
Framework) if the framework is merged to mainline somehow.

> The display ops are better suited
> there, since it's the actual encoder/connector. I hope to get that
> posted this week.
>

I will look forward to that posting. :)

>
>>> +
>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>> + struct i2c_client *client, struct device_node *node)
>>> +{
>>> + int ret;
>>> + struct drm_bridge *bridge;
>>> + struct ptn3460_bridge *ptn_bridge;
>>> +
>>> + bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
>>> + if (!bridge) {
>>> + DRM_ERROR("Failed to allocate drm bridge\n");
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
>>> + if (!ptn_bridge) {
>>> + DRM_ERROR("Failed to allocate ptn bridge\n");
>>> + return -ENOMEM;
>>> + }
>>> +
>>> + ptn_bridge->client = client;
>>> + ptn_bridge->encoder = encoder;
>>> + ptn_bridge->bridge = bridge;
>>> + ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
>>
>> Also, if a regulator is used instead?
>>
>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
>>> + ret = gpio_request_one(ptn_bridge->gpio_pd_n,
>>> + GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
>>> + if (ret) {
>>> + DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
>>> + return ret;
>>> + }
>>> + }
>>> +
>>> + ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>>> + /*
>>> + * Request the reset pin low to avoid the bridge being
>>> + * initialized prematurely
>>> + */
>>> + ret = gpio_request_one(ptn_bridge->gpio_rst_n,
>>> + GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
>>> + if (ret) {
>>> + DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>> + return ret;
>>> + }
>>> + }
>>> +
>>> + ret = of_property_read_u32(node, "edid-emulation",
>>> + &ptn_bridge->edid_emulation);
>>> + if (ret) {
>>> + DRM_ERROR("Can't read edid emulation value\n");
>>> + goto err;
>>> + }
>>> +
>>> + ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
>>> + if (ret) {
>>> + DRM_ERROR("Failed to initialize bridge with drm\n");
>>> + goto err;
>>> + }
>>> +
>>> + bridge->driver_private = ptn_bridge;
>>> + encoder->bridge = bridge;
>>> +
>>> + ret = drm_connector_init(dev, &ptn_bridge->connector,
>>> + &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
>>
>> So it seems that here's not right place to call drm_connector_init function.
>>
>> Display pipeline path could be one of,
>> Display Controller Display bus
>> ---------------------------------------------------------------------------
>> FIMD---------------------LVDS--------------------LCD,
>> or
>> FIMD----------------------eDP---------------------LCD,
>> or
>> FIMD------------------MIPI-DSI------------------LCD,
>> or
>> FIMD-------------------------------------------------LCD
>>
>> And also in case using image enhancement chip,
>> mDNIe-------------FIMD-LITE between Display Controller and Display
>> bus.
>>
>> So, wouldn't the right place below FIMD driver? :)
>>
>
> Well, this driver should be considered outside of exynos context since
> it could be used by any drm driver.
>
> In the exynos context, the right place to implement it would be in the
> dp driver, actually. However, the exynos driver has a level of
> abstraction on top of the crtcs/encoders such that we need to
> initialize it in the exynos_drm_core. The patchset I mentioned above
> should help move things in a direction where fimd/mixer implement
> drm_crtc directly and hdmi/dp implement drm_encoder/drm_connector
> directly. In that world, DP would initialize the ptn driver.
>
>>
>>> + if (ret) {
>>> + DRM_ERROR("Failed to initialize connector with drm\n");
>>> + goto err;
>>> + }
>>> + drm_connector_helper_add(&ptn_bridge->connector,
>>> + &ptn3460_connector_helper_funcs);
>>> + drm_sysfs_connector_add(&ptn_bridge->connector);
>>> + drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
>>> +
>>> + return 0;
>>> +
>>> +err:
>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>> + gpio_free(ptn_bridge->gpio_rst_n);
>>> + return ret;
>>> +}
>>> diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
>>> new file mode 100644
>>> index 0000000..157ffa1
>>> --- /dev/null
>>> +++ b/include/drm/bridge/ptn3460.h
>>> @@ -0,0 +1,36 @@
>>> +/*
>>> + * Copyright (C) 2013 Google, Inc.
>>> + *
>>> + * 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 _DRM_BRIDGE_PTN3460_H_
>>> +#define _DRM_BRIDGE_PTN3460_H_
>>> +
>>> +struct drm_device;
>>> +struct drm_encoder;
>>> +struct i2c_client;
>>> +struct device_node;
>>> +
>>> +#ifdef CONFIG_DRM_PTN3460
>>> +
>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>> + struct i2c_client *client, struct device_node *node);
>>> +#else
>>> +
>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>> + struct i2c_client *client, struct device_node *node)
>>> +{
>>> + return 0;
>>> +}
>>> +
>>> +#endif
>>> +
>>> +#endif
>>> --
>>> 1.8.4
>>>
>>>
>>> _______________________________________________
>>> linux-arm-kernel mailing list
>>> [email protected]
>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> _______________________________________________
> dri-devel mailing list
> [email protected]
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

2013-10-03 18:01:35

by Olof Johansson

[permalink] [raw]
Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

On Thu, Oct 3, 2013 at 10:39 AM, Inki Dae <[email protected]> wrote:
> 2013/10/3 Sean Paul <[email protected]>:
>> On Thu, Oct 3, 2013 at 9:55 AM, Inki Dae <[email protected]> wrote:
>>> Can a regulator be used instead of gpio in other board case?
>>>
>>
>> No, not to my knowledge.
>>
>
> Hm.. plz check it out again. the gpio pin is specific to board, and
> the the gpio be used as power source trigger could be replaced with a
> regulator according to board design. So you should consider all
> possibilities even though there are no other cases yet: other board
> could use a regulator instead.

Take a look at the data sheet, it is publicly available.

PD_N is not a power supply input, so modelling it as a regulator makes no sense:

"If PD_N is LOW, then the device is in Deep power-down completely,
even if supply rail is ON; for the device to be able to operate, the
PD_N pin must be HIGH."



-Olof

2013-10-03 18:10:22

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

On Thu, Oct 3, 2013 at 1:39 PM, Inki Dae <[email protected]> wrote:
> 2013/10/3 Sean Paul <[email protected]>:
>> On Thu, Oct 3, 2013 at 9:55 AM, Inki Dae <[email protected]> wrote:
>>> Hi, thank you for your contribution and the below is my short comments,
>>>
>>> 2013/10/2 Sean Paul <[email protected]>:
>>>> This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
>>>> bridge chip.
>>>>
>>>> Signed-off-by: Sean Paul <[email protected]>
>>>> ---
>>>> .../devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++
>>>> drivers/gpu/drm/Kconfig | 2 +
>>>> drivers/gpu/drm/Makefile | 1 +
>>>> drivers/gpu/drm/bridge/Kconfig | 4 +
>>>> drivers/gpu/drm/bridge/Makefile | 3 +
>>>> drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++
>>>> include/drm/bridge/ptn3460.h | 36 +++
>>>> 7 files changed, 422 insertions(+)
>>>> create mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>> create mode 100644 drivers/gpu/drm/bridge/Kconfig
>>>> create mode 100644 drivers/gpu/drm/bridge/Makefile
>>>> create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
>>>> create mode 100644 include/drm/bridge/ptn3460.h
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>> new file mode 100644
>>>> index 0000000..c1cd329
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>> @@ -0,0 +1,27 @@
>>>> +ptn3460-bridge bindings
>>>> +
>>>> +Required properties:
>>>> + - compatible: "nxp,ptn3460"
>>>> + - reg: i2c address of the bridge
>>>> + - powerdown-gpio: OF device-tree gpio specification
>>>
>>> Can a regulator be used instead of gpio in other board case?
>>>
>>
>> No, not to my knowledge.
>>
>
> Hm.. plz check it out again. the gpio pin is specific to board, and
> the the gpio be used as power source trigger could be replaced with a
> regulator according to board design. So you should consider all
> possibilities even though there are no other cases yet: other board
> could use a regulator instead.
>
>>
>>>> + - reset-gpio: OF device-tree gpio specification
>>>> + - edid-emulation: The EDID emulation entry to use
>>>> + +-------+------------+------------------+
>>>> + | Value | Resolution | Description |
>>>> + | 0 | 1024x768 | NXP Generic |
>>>> + | 1 | 1920x1080 | NXP Generic |
>>>> + | 2 | 1920x1080 | NXP Generic |
>>>> + | 3 | 1600x900 | Samsung LTM200KT |
>>>> + | 4 | 1920x1080 | Samsung LTM230HT |
>>>> + | 5 | 1366x768 | NXP Generic |
>>>> + | 6 | 1600x900 | ChiMei M215HGE |
>>>> + +-------+------------+------------------+
>>>> +
>>>> +Example:
>>>> + ptn3460-bridge@20 {
>>>> + compatible = "nxp,ptn3460";
>>>> + reg = <0x20>;
>>>> + powerdown-gpio = <&gpy2 5 1 0 0>;
>>>> + reset-gpio = <&gpx1 5 1 0 0>;
>>>> + edid-emulation = <5>;
>>>> + };
>>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>>> index 955555d..cd7bfb3 100644
>>>> --- a/drivers/gpu/drm/Kconfig
>>>> +++ b/drivers/gpu/drm/Kconfig
>>>> @@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
>>>> source "drivers/gpu/drm/qxl/Kconfig"
>>>>
>>>> source "drivers/gpu/drm/msm/Kconfig"
>>>> +
>>>> +source "drivers/gpu/drm/bridge/Kconfig"
>>>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>>>> index f089adf..9234253 100644
>>>> --- a/drivers/gpu/drm/Makefile
>>>> +++ b/drivers/gpu/drm/Makefile
>>>> @@ -56,3 +56,4 @@ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
>>>> obj-$(CONFIG_DRM_QXL) += qxl/
>>>> obj-$(CONFIG_DRM_MSM) += msm/
>>>> obj-y += i2c/
>>>> +obj-y += bridge/
>>>> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
>>>> new file mode 100644
>>>> index 0000000..f8db069
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/bridge/Kconfig
>>>> @@ -0,0 +1,4 @@
>>>> +config DRM_PTN3460
>>>> + tristate "PTN3460 DP/LVDS bridge"
>>>> + depends on DRM && I2C
>>>> + ---help---
>>>> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
>>>> new file mode 100644
>>>> index 0000000..b4733e1
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/bridge/Makefile
>>>> @@ -0,0 +1,3 @@
>>>> +ccflags-y := -Iinclude/drm
>>>> +
>>>> +obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>>>> diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
>>>> new file mode 100644
>>>> index 0000000..a9e5c1a
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/bridge/ptn3460.c
>>>> @@ -0,0 +1,349 @@
>>>> +/*
>>>> + * NXP PTN3460 DP/LVDS bridge driver
>>>> + *
>>>> + * Copyright (C) 2013 Google, Inc.
>>>> + *
>>>> + * 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/of.h>
>>>> +#include <linux/of_gpio.h>
>>>> +#include <linux/i2c.h>
>>>> +#include <linux/gpio.h>
>>>> +#include <linux/delay.h>
>>>> +
>>>> +#include "drmP.h"
>>>> +#include "drm_edid.h"
>>>> +#include "drm_crtc.h"
>>>> +#include "drm_crtc_helper.h"
>>>> +
>>>> +#include "bridge/ptn3460.h"
>>>> +
>>>> +#define PTN3460_EDID_ADDR 0x0
>>>> +#define PTN3460_EDID_EMULATION_ADDR 0x84
>>>> +#define PTN3460_EDID_ENABLE_EMULATION 0
>>>> +#define PTN3460_EDID_EMULATION_SELECTION 1
>>>> +#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
>>>> +
>>>> +struct ptn3460_bridge {
>>>> + struct drm_connector connector;
>>>> + struct i2c_client *client;
>>>> + struct drm_encoder *encoder;
>>>> + struct drm_bridge *bridge;
>>>> + struct edid *edid;
>>>> + int gpio_pd_n;
>>>> + int gpio_rst_n;
>>>> + u32 edid_emulation;
>>>> + bool enabled;
>>>> +};
>>>> +
>>>> +static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
>>>> + u8 *buf, int len)
>>>> +{
>>>> + int ret;
>>>> +
>>>> + ret = i2c_master_send(ptn_bridge->client, &addr, 1);
>>>> + if (ret <= 0) {
>>>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = i2c_master_recv(ptn_bridge->client, buf, len);
>>>> + if (ret <= 0) {
>>>> + DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
>>>> + char val)
>>>> +{
>>>> + int ret;
>>>> + char buf[2];
>>>> +
>>>> + buf[0] = addr;
>>>> + buf[1] = val;
>>>> +
>>>> + ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
>>>> + if (ret <= 0) {
>>>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
>>>> +{
>>>> + int ret;
>>>> + char val;
>>>> +
>>>> + /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
>>>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
>>>> + ptn_bridge->edid_emulation);
>>>> + if (ret) {
>>>> + DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + /* Enable EDID emulation and select the desired EDID */
>>>> + val = 1 << PTN3460_EDID_ENABLE_EMULATION |
>>>> + ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
>>>> +
>>>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
>>>> + if (ret) {
>>>> + DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static void ptn3460_pre_enable(struct drm_bridge *bridge)
>>>> +{
>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>> + int ret;
>>>> +
>>>> + if (ptn_bridge->enabled)
>>>> + return;
>>>> +
>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>> + gpio_set_value(ptn_bridge->gpio_pd_n, 1);
>>>
>>> Ditto.
>>>
>>>> +
>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 0);
>>>> + udelay(10);
>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>>> + }
>>>> +
>>>> + /*
>>>> + * There's a bug in the PTN chip where it falsely asserts hotplug before
>>>> + * it is fully functional. We're forced to wait for the maximum start up
>>>> + * time specified in the chip's datasheet to make sure we're really up.
>>>> + */
>>>> + msleep(90);
>>>> +
>>>> + ret = ptn3460_select_edid(ptn_bridge);
>>>> + if (ret)
>>>> + DRM_ERROR("Select edid failed ret=%d\n", ret);
>>>> +
>>>> + ptn_bridge->enabled = true;
>>>> +}
>>>> +
>>>> +static void ptn3460_enable(struct drm_bridge *bridge)
>>>> +{
>>>> +}
>>>> +
>>>> +static void ptn3460_disable(struct drm_bridge *bridge)
>>>> +{
>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>> +
>>>> + if (!ptn_bridge->enabled)
>>>> + return;
>>>> +
>>>> + ptn_bridge->enabled = false;
>>>> +
>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>>> +
>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>> + gpio_set_value(ptn_bridge->gpio_pd_n, 0);
>>>
>>> Ditto.
>>>
>>>> +}
>>>> +
>>>> +static void ptn3460_post_disable(struct drm_bridge *bridge)
>>>> +{
>>>> +}
>>>> +
>>>> +void ptn3460_bridge_destroy(struct drm_bridge *bridge)
>>>> +{
>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>> +
>>>> + drm_bridge_cleanup(bridge);
>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>
>>> Ditto.
>>>
>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>> + gpio_free(ptn_bridge->gpio_rst_n);
>>>> + /* Nothing else to free, we've got devm allocated memory */
>>>> +}
>>>> +
>>>> +struct drm_bridge_funcs ptn3460_bridge_funcs = {
>>>> + .pre_enable = ptn3460_pre_enable,
>>>> + .enable = ptn3460_enable,
>>>> + .disable = ptn3460_disable,
>>>> + .post_disable = ptn3460_post_disable,
>>>> + .destroy = ptn3460_bridge_destroy,
>>>> +};
>>>> +
>>>> +int ptn3460_get_modes(struct drm_connector *connector)
>>>> +{
>>>> + struct ptn3460_bridge *ptn_bridge;
>>>> + u8 *edid;
>>>> + int ret, num_modes;
>>>> + bool power_off;
>>>> +
>>>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>>>> +
>>>> + if (ptn_bridge->edid)
>>>> + return drm_add_edid_modes(connector, ptn_bridge->edid);
>>>> +
>>>> + power_off = !ptn_bridge->enabled;
>>>> + ptn3460_pre_enable(ptn_bridge->bridge);
>>>> +
>>>> + edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
>>>> + if (!edid) {
>>>> + DRM_ERROR("Failed to allocate edid\n");
>>>> + return 0;
>>>> + }
>>>> +
>>>> + ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
>>>> + EDID_LENGTH);
>>>> + if (ret) {
>>>> + kfree(edid);
>>>> + num_modes = 0;
>>>> + goto out;
>>>> + }
>>>> +
>>>> + ptn_bridge->edid = (struct edid *)edid;
>>>> + drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
>>>> +
>>>> + num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
>>>> +
>>>> +out:
>>>> + if (power_off)
>>>> + ptn3460_disable(ptn_bridge->bridge);
>>>> +
>>>> + return num_modes;
>>>> +}
>>>> +
>>>> +static int ptn3460_mode_valid(struct drm_connector *connector,
>>>> + struct drm_display_mode *mode)
>>>> +{
>>>> + return MODE_OK;
>>>> +}
>>>> +
>>>> +struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
>>>> +{
>>>> + struct ptn3460_bridge *ptn_bridge;
>>>> +
>>>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>>>> +
>>>> + return ptn_bridge->encoder;
>>>> +}
>>>> +
>>>> +struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
>>>> + .get_modes = ptn3460_get_modes,
>>>> + .mode_valid = ptn3460_mode_valid,
>>>> + .best_encoder = ptn3460_best_encoder,
>>>> +};
>>>> +
>>>> +enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
>>>> + bool force)
>>>> +{
>>>> + return connector_status_connected;
>>>> +}
>>>> +
>>>> +void ptn3460_connector_destroy(struct drm_connector *connector)
>>>> +{
>>>> + drm_connector_cleanup(connector);
>>>> +}
>>>> +
>>>> +struct drm_connector_funcs ptn3460_connector_funcs = {
>>>> + .dpms = drm_helper_connector_dpms,
>>>> + .fill_modes = drm_helper_probe_single_connector_modes,
>>>> + .detect = ptn3460_detect,
>>>> + .destroy = ptn3460_connector_destroy,
>>>> +};
>>>
>>> Why do you try to add a new connector here? We already have the
>>> connector for LCD, and also provides some callbacks for it. For this,
>>> please see exynos_drm_display_ops of exynos_drm_fimd driver, and you
>>> can add new callbacks to there such as init callback for bridge device
>>> initialization if needed.
>>>
>>
>> We add a new connector for 2 reasons:
>>
>> 1) We need to override the drm detect() callback to always return true
>> since the DP driver will presumably return its hotplug status which
>> will always be low when the ptn chip is turned off.
>> 2) We want the ability to control the result of get_modes().
>>
>> I've got a patch set almost ready to tear the display ops out of fimd
>> and put them in the DP driver.
>
> Really? if so, that is ideal something we want and we should go. But
> isn't the DP driver placed in drivers/video/exynos? How did you take
> care of that?

git mv :)

> Actually, for this, we planned to use CDF(Common Display
> Framework) if the framework is merged to mainline somehow.
>

Right. I think CDF will end up being a series of improvements to drm,
as opposed to its own framework (at least this was the conclusion I
came to after speaking with Laurent at the plumbers conference). I
don't think it makes sense to have the DP driver outside of drm. The
HDMI driver is already inside drm, the DP driver should be too.
Regardless, this conversation is only tangentially related to this
patch and can probably be deferred.

Sean

>> The display ops are better suited
>> there, since it's the actual encoder/connector. I hope to get that
>> posted this week.
>>
>
> I will look forward to that posting. :)
>
>>
>>>> +
>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>> + struct i2c_client *client, struct device_node *node)
>>>> +{
>>>> + int ret;
>>>> + struct drm_bridge *bridge;
>>>> + struct ptn3460_bridge *ptn_bridge;
>>>> +
>>>> + bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
>>>> + if (!bridge) {
>>>> + DRM_ERROR("Failed to allocate drm bridge\n");
>>>> + return -ENOMEM;
>>>> + }
>>>> +
>>>> + ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
>>>> + if (!ptn_bridge) {
>>>> + DRM_ERROR("Failed to allocate ptn bridge\n");
>>>> + return -ENOMEM;
>>>> + }
>>>> +
>>>> + ptn_bridge->client = client;
>>>> + ptn_bridge->encoder = encoder;
>>>> + ptn_bridge->bridge = bridge;
>>>> + ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
>>>
>>> Also, if a regulator is used instead?
>>>
>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
>>>> + ret = gpio_request_one(ptn_bridge->gpio_pd_n,
>>>> + GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
>>>> + if (ret) {
>>>> + DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
>>>> + return ret;
>>>> + }
>>>> + }
>>>> +
>>>> + ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>>>> + /*
>>>> + * Request the reset pin low to avoid the bridge being
>>>> + * initialized prematurely
>>>> + */
>>>> + ret = gpio_request_one(ptn_bridge->gpio_rst_n,
>>>> + GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
>>>> + if (ret) {
>>>> + DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>> + return ret;
>>>> + }
>>>> + }
>>>> +
>>>> + ret = of_property_read_u32(node, "edid-emulation",
>>>> + &ptn_bridge->edid_emulation);
>>>> + if (ret) {
>>>> + DRM_ERROR("Can't read edid emulation value\n");
>>>> + goto err;
>>>> + }
>>>> +
>>>> + ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
>>>> + if (ret) {
>>>> + DRM_ERROR("Failed to initialize bridge with drm\n");
>>>> + goto err;
>>>> + }
>>>> +
>>>> + bridge->driver_private = ptn_bridge;
>>>> + encoder->bridge = bridge;
>>>> +
>>>> + ret = drm_connector_init(dev, &ptn_bridge->connector,
>>>> + &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
>>>
>>> So it seems that here's not right place to call drm_connector_init function.
>>>
>>> Display pipeline path could be one of,
>>> Display Controller Display bus
>>> ---------------------------------------------------------------------------
>>> FIMD---------------------LVDS--------------------LCD,
>>> or
>>> FIMD----------------------eDP---------------------LCD,
>>> or
>>> FIMD------------------MIPI-DSI------------------LCD,
>>> or
>>> FIMD-------------------------------------------------LCD
>>>
>>> And also in case using image enhancement chip,
>>> mDNIe-------------FIMD-LITE between Display Controller and Display
>>> bus.
>>>
>>> So, wouldn't the right place below FIMD driver? :)
>>>
>>
>> Well, this driver should be considered outside of exynos context since
>> it could be used by any drm driver.
>>
>> In the exynos context, the right place to implement it would be in the
>> dp driver, actually. However, the exynos driver has a level of
>> abstraction on top of the crtcs/encoders such that we need to
>> initialize it in the exynos_drm_core. The patchset I mentioned above
>> should help move things in a direction where fimd/mixer implement
>> drm_crtc directly and hdmi/dp implement drm_encoder/drm_connector
>> directly. In that world, DP would initialize the ptn driver.
>>
>>>
>>>> + if (ret) {
>>>> + DRM_ERROR("Failed to initialize connector with drm\n");
>>>> + goto err;
>>>> + }
>>>> + drm_connector_helper_add(&ptn_bridge->connector,
>>>> + &ptn3460_connector_helper_funcs);
>>>> + drm_sysfs_connector_add(&ptn_bridge->connector);
>>>> + drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
>>>> +
>>>> + return 0;
>>>> +
>>>> +err:
>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>> + gpio_free(ptn_bridge->gpio_rst_n);
>>>> + return ret;
>>>> +}
>>>> diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
>>>> new file mode 100644
>>>> index 0000000..157ffa1
>>>> --- /dev/null
>>>> +++ b/include/drm/bridge/ptn3460.h
>>>> @@ -0,0 +1,36 @@
>>>> +/*
>>>> + * Copyright (C) 2013 Google, Inc.
>>>> + *
>>>> + * 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 _DRM_BRIDGE_PTN3460_H_
>>>> +#define _DRM_BRIDGE_PTN3460_H_
>>>> +
>>>> +struct drm_device;
>>>> +struct drm_encoder;
>>>> +struct i2c_client;
>>>> +struct device_node;
>>>> +
>>>> +#ifdef CONFIG_DRM_PTN3460
>>>> +
>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>> + struct i2c_client *client, struct device_node *node);
>>>> +#else
>>>> +
>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>> + struct i2c_client *client, struct device_node *node)
>>>> +{
>>>> + return 0;
>>>> +}
>>>> +
>>>> +#endif
>>>> +
>>>> +#endif
>>>> --
>>>> 1.8.4
>>>>
>>>>
>>>> _______________________________________________
>>>> linux-arm-kernel mailing list
>>>> [email protected]
>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>> _______________________________________________
>> dri-devel mailing list
>> [email protected]
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

Subject: Re: [PATCH 4/5] drm/exynos: Initialize ptn3460 if present

2013/10/4 Sean Paul <[email protected]>:
> On Thu, Oct 3, 2013 at 1:18 PM, Inki Dae <[email protected]> wrote:
>> 2013/10/4 Sean Paul <[email protected]>:
>>> On Thu, Oct 3, 2013 at 10:43 AM, Inki Dae <[email protected]> wrote:
>>>> 2013/10/2 Sean Paul <[email protected]>:
>>>>> This patch adds code to look for the ptn3460 in the device tree file on
>>>>> exynos initialization. If ptn node is found, the driver will initialize
>>>>> the ptn3460 driver and skip creating a DP connector (since the bridge
>>>>> driver will register its own connector).
>>>>>
>>>>> Signed-off-by: Sean Paul <[email protected]>
>>>>> ---
>>>>> drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
>>>>> 1 file changed, 43 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
>>>>> index 1bef6dc..9cf4476 100644
>>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
>>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
>>>>> @@ -12,7 +12,9 @@
>>>>> * option) any later version.
>>>>> */
>>>>>
>>>>> +#include <linux/of_i2c.h>
>>>>> #include <drm/drmP.h>
>>>>> +#include <drm/bridge/ptn3460.h>
>>>>> #include "exynos_drm_drv.h"
>>>>> #include "exynos_drm_encoder.h"
>>>>> #include "exynos_drm_connector.h"
>>>>> @@ -20,6 +22,40 @@
>>>>>
>>>>> static LIST_HEAD(exynos_drm_subdrv_list);
>>>>>
>>>>> +struct bridge_init {
>>>>> + struct i2c_client *client;
>>>>> + struct device_node *node;
>>>>> +};
>>>>> +
>>>>> +static bool find_bridge(const char *name, struct bridge_init *bridge)
>>>>> +{
>>>>> + bridge->client = NULL;
>>>>> + bridge->node = of_find_node_by_name(NULL, name);
>>>>
>>>> Not clear to me. Why do you try to handle device tree here, not real
>>>> device driver?. How about adding a output property to board specific
>>>> fimd dt node: i.e. output = <&ptn3460_bridge>?
>>>
>>> The problem doing something like this is that we won't have a handle
>>> to drm_device if it's just a standalone driver, and so we won't be
>>> able to register the bridge or connector. We need this init call one
>>> way or another.
>>>
>>
>> At least, dt binding shoul be done in real device driver so this way
>> is not good. Let's find a better way.
>>
>
> Right, so this is kind of tricky. If you do it in a "real" device
> driver, you end up parsing the dt stuff in the probe, and then racing
> the init callback. I figured it would be best just to do everything in
> one place without races.
>
> Hopefully I'm just missing a good way to solve this problem, any concrete ideas?
>
>>>
>>>> Actually, the output
>>>> device of fimd hw could be one of MIPI-DSI, eDP, mDNIe, LVDS bridge,
>>>> or LCD. And then, let's find ptn3460-bridge node in the fimd driver,
>>>> and initialize the ptn3460 bridge driver, and get power on or off
>>>> through exynos_drm_display_ops of the fimd driver. And all these
>>>> codes could be hided from fimd driver by moving them into
>>>> exynos_drm_display_ops. Of course, for this, you would need additional
>>>> works. So let's do it if needed.
>>>>
>>>> The below is the outline of device tree I recommend,
>>>>
>>>> In board dts,
>>>> i2c@I2CD000 {
>>>> ptn3460_bridge: prn3460-bridge@20 {
>>>> ...
>>>> }
>>>> }
>>>>
>>>> fimd@11c00000 {
>>>> ...
>>>> output_dev = <&ptn3460_bridge>;
>>>> }
>>>>
>>>> With this, I believe that you can do all things you want for
>>>> controlling the LVDS bridge in fimd driver.
>>>>
>>>
>>> No, this isn't what I want to do. The bridge should not hang off fimd
>>> since it's a crtc. The bridge should only be initialized by the DP
>>> driver (it doesn't make sense to initialize ptn when you're using
>>> MIPI/LVDS/whatever).
>>
>> I don't mean that the bridge device should be initialized by fimd
>> directly but the fimd driver provides just interfaces abstracted to
>> control the bridge device. And basically, the exynos_drm_display_ops
>> shouldn't be in fimd driver but in real connector driver; i.e. lcd
>> panel or LVDS driver. The reason I placed the exynos_drm_display_ops
>> in fimd driver is that lcd panel driver is controlled by lcd class
>> depended on Linux framebuffer, and I thought the panel driver should
>> be shared with drm driver in case of ARM SoC. The
>> exynos_drm_display_ops should be moved into right place if something
>> better exists some time or other.
>>
>> So how can the DP driver control the bridge device as of now? the DP
>> you mentioned would be eDP, and the eDP driver is placed in
>> drivers/video/exynos/, and also MIPI-DSI driver.
>>
>
> It can't. The DP driver just operates on its own and display either
> comes up or it doesn't depending on whether fimd happens to be
> initialized first.

Ok, I don't know the DP hardware well. But, MIPI-DSI driver is
depending on fimd on/off ordering. ie. to enable display hw pipe, the
ordering should be FIMD----MIPI-DSI-----LCD because initial commands
_cannot be set_ to lcd panel if fimd is off. And to disable that, the
ordering should be LCD-------MIPI-DSI-------FIMD in same reason: to
get lcd panel off, off commands should be set to lcd panel. In similar
reason, I had posted FB_EARLY_EVENT_BLANK feature to mainline and that
have been merged.

> As I mentioned earlier, I have a patch set which
> moves DP driver into drm/exynos and removes the display_ops from fimd.
> That will fix this issue.

Ah... as you may know I mentioned about this issue some time ago;
moving eDP driver(maybe??) into drm/exynos. At that time, I had
rejected it because I thought we should share these bus drivers with
Linux framebuffer driver: actually, I planned to use CDF for this.
But.... I'm not sure even CDF as of now. So let's have a discussion
about this issue with other people: it doesn't mean I oppose your
patch set.

>
> Sean
>
>
>>>
>>> Since the actual crtc/encoder drivers are abstracted through
>>> exynos_drm_core/crtc/encoder, we need to initialize the ptn driver in
>>> the abstraction layers in order to hook it directly into drm.
>>>
>>> Sean
>>>
>>>
>>>> Thanks,
>>>> Inki Dae
>>>>
>>>>> + if (!bridge->node)
>>>>> + return false;
>>>>> +
>>>>> + bridge->client = of_find_i2c_device_by_node(bridge->node);
>>>>> + if (!bridge->client)
>>>>> + return false;
>>>>> +
>>>>> + return true;
>>>>> +}
>>>>> +
>>>>> +/* returns the number of bridges attached */
>>>>> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>>>>> + struct drm_encoder *encoder)
>>>>> +{
>>>>> + struct bridge_init bridge;
>>>>> + int ret;
>>>>> +
>>>>> + if (find_bridge("ptn3460-bridge", &bridge)) {
>>>>> + ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
>>>>> + if (!ret)
>>>>> + return 1;
>>>>> + }
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>>>> struct exynos_drm_subdrv *subdrv)
>>>>> {
>>>>> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>>>> DRM_ERROR("failed to create encoder\n");
>>>>> return -EFAULT;
>>>>> }
>>>>> + subdrv->encoder = encoder;
>>>>> +
>>>>> + if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
>>>>> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
>>>>> + if (ret)
>>>>> + return 0;
>>>>> + }
>>>>>
>>>>> /*
>>>>> * create and initialize a connector for this sub driver and
>>>>> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>>>> goto err_destroy_encoder;
>>>>> }
>>>>>
>>>>> - subdrv->encoder = encoder;
>>>>> subdrv->connector = connector;
>>>>>
>>>>> return 0;
>>>>> --
>>>>> 1.8.4
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> linux-arm-kernel mailing list
>>>>> [email protected]
>>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>> _______________________________________________
>>> dri-devel mailing list
>>> [email protected]
>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> _______________________________________________
> dri-devel mailing list
> [email protected]
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

2013/10/4 Sean Paul <[email protected]>:
> On Thu, Oct 3, 2013 at 1:39 PM, Inki Dae <[email protected]> wrote:
>> 2013/10/3 Sean Paul <[email protected]>:
>>> On Thu, Oct 3, 2013 at 9:55 AM, Inki Dae <[email protected]> wrote:
>>>> Hi, thank you for your contribution and the below is my short comments,
>>>>
>>>> 2013/10/2 Sean Paul <[email protected]>:
>>>>> This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
>>>>> bridge chip.
>>>>>
>>>>> Signed-off-by: Sean Paul <[email protected]>
>>>>> ---
>>>>> .../devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++
>>>>> drivers/gpu/drm/Kconfig | 2 +
>>>>> drivers/gpu/drm/Makefile | 1 +
>>>>> drivers/gpu/drm/bridge/Kconfig | 4 +
>>>>> drivers/gpu/drm/bridge/Makefile | 3 +
>>>>> drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++
>>>>> include/drm/bridge/ptn3460.h | 36 +++
>>>>> 7 files changed, 422 insertions(+)
>>>>> create mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>>> create mode 100644 drivers/gpu/drm/bridge/Kconfig
>>>>> create mode 100644 drivers/gpu/drm/bridge/Makefile
>>>>> create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
>>>>> create mode 100644 include/drm/bridge/ptn3460.h
>>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>>> new file mode 100644
>>>>> index 0000000..c1cd329
>>>>> --- /dev/null
>>>>> +++ b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>>> @@ -0,0 +1,27 @@
>>>>> +ptn3460-bridge bindings
>>>>> +
>>>>> +Required properties:
>>>>> + - compatible: "nxp,ptn3460"
>>>>> + - reg: i2c address of the bridge
>>>>> + - powerdown-gpio: OF device-tree gpio specification
>>>>
>>>> Can a regulator be used instead of gpio in other board case?
>>>>
>>>
>>> No, not to my knowledge.
>>>
>>
>> Hm.. plz check it out again. the gpio pin is specific to board, and
>> the the gpio be used as power source trigger could be replaced with a
>> regulator according to board design. So you should consider all
>> possibilities even though there are no other cases yet: other board
>> could use a regulator instead.
>>
>>>
>>>>> + - reset-gpio: OF device-tree gpio specification
>>>>> + - edid-emulation: The EDID emulation entry to use
>>>>> + +-------+------------+------------------+
>>>>> + | Value | Resolution | Description |
>>>>> + | 0 | 1024x768 | NXP Generic |
>>>>> + | 1 | 1920x1080 | NXP Generic |
>>>>> + | 2 | 1920x1080 | NXP Generic |
>>>>> + | 3 | 1600x900 | Samsung LTM200KT |
>>>>> + | 4 | 1920x1080 | Samsung LTM230HT |
>>>>> + | 5 | 1366x768 | NXP Generic |
>>>>> + | 6 | 1600x900 | ChiMei M215HGE |
>>>>> + +-------+------------+------------------+
>>>>> +
>>>>> +Example:
>>>>> + ptn3460-bridge@20 {
>>>>> + compatible = "nxp,ptn3460";
>>>>> + reg = <0x20>;
>>>>> + powerdown-gpio = <&gpy2 5 1 0 0>;
>>>>> + reset-gpio = <&gpx1 5 1 0 0>;
>>>>> + edid-emulation = <5>;
>>>>> + };
>>>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>>>> index 955555d..cd7bfb3 100644
>>>>> --- a/drivers/gpu/drm/Kconfig
>>>>> +++ b/drivers/gpu/drm/Kconfig
>>>>> @@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
>>>>> source "drivers/gpu/drm/qxl/Kconfig"
>>>>>
>>>>> source "drivers/gpu/drm/msm/Kconfig"
>>>>> +
>>>>> +source "drivers/gpu/drm/bridge/Kconfig"
>>>>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>>>>> index f089adf..9234253 100644
>>>>> --- a/drivers/gpu/drm/Makefile
>>>>> +++ b/drivers/gpu/drm/Makefile
>>>>> @@ -56,3 +56,4 @@ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
>>>>> obj-$(CONFIG_DRM_QXL) += qxl/
>>>>> obj-$(CONFIG_DRM_MSM) += msm/
>>>>> obj-y += i2c/
>>>>> +obj-y += bridge/
>>>>> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
>>>>> new file mode 100644
>>>>> index 0000000..f8db069
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/bridge/Kconfig
>>>>> @@ -0,0 +1,4 @@
>>>>> +config DRM_PTN3460
>>>>> + tristate "PTN3460 DP/LVDS bridge"
>>>>> + depends on DRM && I2C
>>>>> + ---help---
>>>>> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
>>>>> new file mode 100644
>>>>> index 0000000..b4733e1
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/bridge/Makefile
>>>>> @@ -0,0 +1,3 @@
>>>>> +ccflags-y := -Iinclude/drm
>>>>> +
>>>>> +obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>>>>> diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
>>>>> new file mode 100644
>>>>> index 0000000..a9e5c1a
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/bridge/ptn3460.c
>>>>> @@ -0,0 +1,349 @@
>>>>> +/*
>>>>> + * NXP PTN3460 DP/LVDS bridge driver
>>>>> + *
>>>>> + * Copyright (C) 2013 Google, Inc.
>>>>> + *
>>>>> + * 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/of.h>
>>>>> +#include <linux/of_gpio.h>
>>>>> +#include <linux/i2c.h>
>>>>> +#include <linux/gpio.h>
>>>>> +#include <linux/delay.h>
>>>>> +
>>>>> +#include "drmP.h"
>>>>> +#include "drm_edid.h"
>>>>> +#include "drm_crtc.h"
>>>>> +#include "drm_crtc_helper.h"
>>>>> +
>>>>> +#include "bridge/ptn3460.h"
>>>>> +
>>>>> +#define PTN3460_EDID_ADDR 0x0
>>>>> +#define PTN3460_EDID_EMULATION_ADDR 0x84
>>>>> +#define PTN3460_EDID_ENABLE_EMULATION 0
>>>>> +#define PTN3460_EDID_EMULATION_SELECTION 1
>>>>> +#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
>>>>> +
>>>>> +struct ptn3460_bridge {
>>>>> + struct drm_connector connector;
>>>>> + struct i2c_client *client;
>>>>> + struct drm_encoder *encoder;
>>>>> + struct drm_bridge *bridge;
>>>>> + struct edid *edid;
>>>>> + int gpio_pd_n;
>>>>> + int gpio_rst_n;
>>>>> + u32 edid_emulation;
>>>>> + bool enabled;
>>>>> +};
>>>>> +
>>>>> +static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
>>>>> + u8 *buf, int len)
>>>>> +{
>>>>> + int ret;
>>>>> +
>>>>> + ret = i2c_master_send(ptn_bridge->client, &addr, 1);
>>>>> + if (ret <= 0) {
>>>>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + ret = i2c_master_recv(ptn_bridge->client, buf, len);
>>>>> + if (ret <= 0) {
>>>>> + DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
>>>>> + char val)
>>>>> +{
>>>>> + int ret;
>>>>> + char buf[2];
>>>>> +
>>>>> + buf[0] = addr;
>>>>> + buf[1] = val;
>>>>> +
>>>>> + ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
>>>>> + if (ret <= 0) {
>>>>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
>>>>> +{
>>>>> + int ret;
>>>>> + char val;
>>>>> +
>>>>> + /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
>>>>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
>>>>> + ptn_bridge->edid_emulation);
>>>>> + if (ret) {
>>>>> + DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + /* Enable EDID emulation and select the desired EDID */
>>>>> + val = 1 << PTN3460_EDID_ENABLE_EMULATION |
>>>>> + ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
>>>>> +
>>>>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
>>>>> + if (ret) {
>>>>> + DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static void ptn3460_pre_enable(struct drm_bridge *bridge)
>>>>> +{
>>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>>> + int ret;
>>>>> +
>>>>> + if (ptn_bridge->enabled)
>>>>> + return;
>>>>> +
>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>> + gpio_set_value(ptn_bridge->gpio_pd_n, 1);
>>>>
>>>> Ditto.
>>>>
>>>>> +
>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 0);
>>>>> + udelay(10);
>>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>>>> + }
>>>>> +
>>>>> + /*
>>>>> + * There's a bug in the PTN chip where it falsely asserts hotplug before
>>>>> + * it is fully functional. We're forced to wait for the maximum start up
>>>>> + * time specified in the chip's datasheet to make sure we're really up.
>>>>> + */
>>>>> + msleep(90);
>>>>> +
>>>>> + ret = ptn3460_select_edid(ptn_bridge);
>>>>> + if (ret)
>>>>> + DRM_ERROR("Select edid failed ret=%d\n", ret);
>>>>> +
>>>>> + ptn_bridge->enabled = true;
>>>>> +}
>>>>> +
>>>>> +static void ptn3460_enable(struct drm_bridge *bridge)
>>>>> +{
>>>>> +}
>>>>> +
>>>>> +static void ptn3460_disable(struct drm_bridge *bridge)
>>>>> +{
>>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>>> +
>>>>> + if (!ptn_bridge->enabled)
>>>>> + return;
>>>>> +
>>>>> + ptn_bridge->enabled = false;
>>>>> +
>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>>>> +
>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>> + gpio_set_value(ptn_bridge->gpio_pd_n, 0);
>>>>
>>>> Ditto.
>>>>
>>>>> +}
>>>>> +
>>>>> +static void ptn3460_post_disable(struct drm_bridge *bridge)
>>>>> +{
>>>>> +}
>>>>> +
>>>>> +void ptn3460_bridge_destroy(struct drm_bridge *bridge)
>>>>> +{
>>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>>> +
>>>>> + drm_bridge_cleanup(bridge);
>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>>
>>>> Ditto.
>>>>
>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>>> + gpio_free(ptn_bridge->gpio_rst_n);
>>>>> + /* Nothing else to free, we've got devm allocated memory */
>>>>> +}
>>>>> +
>>>>> +struct drm_bridge_funcs ptn3460_bridge_funcs = {
>>>>> + .pre_enable = ptn3460_pre_enable,
>>>>> + .enable = ptn3460_enable,
>>>>> + .disable = ptn3460_disable,
>>>>> + .post_disable = ptn3460_post_disable,
>>>>> + .destroy = ptn3460_bridge_destroy,
>>>>> +};
>>>>> +
>>>>> +int ptn3460_get_modes(struct drm_connector *connector)
>>>>> +{
>>>>> + struct ptn3460_bridge *ptn_bridge;
>>>>> + u8 *edid;
>>>>> + int ret, num_modes;
>>>>> + bool power_off;
>>>>> +
>>>>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>>>>> +
>>>>> + if (ptn_bridge->edid)
>>>>> + return drm_add_edid_modes(connector, ptn_bridge->edid);
>>>>> +
>>>>> + power_off = !ptn_bridge->enabled;
>>>>> + ptn3460_pre_enable(ptn_bridge->bridge);
>>>>> +
>>>>> + edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
>>>>> + if (!edid) {
>>>>> + DRM_ERROR("Failed to allocate edid\n");
>>>>> + return 0;
>>>>> + }
>>>>> +
>>>>> + ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
>>>>> + EDID_LENGTH);
>>>>> + if (ret) {
>>>>> + kfree(edid);
>>>>> + num_modes = 0;
>>>>> + goto out;
>>>>> + }
>>>>> +
>>>>> + ptn_bridge->edid = (struct edid *)edid;
>>>>> + drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
>>>>> +
>>>>> + num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
>>>>> +
>>>>> +out:
>>>>> + if (power_off)
>>>>> + ptn3460_disable(ptn_bridge->bridge);
>>>>> +
>>>>> + return num_modes;
>>>>> +}
>>>>> +
>>>>> +static int ptn3460_mode_valid(struct drm_connector *connector,
>>>>> + struct drm_display_mode *mode)
>>>>> +{
>>>>> + return MODE_OK;
>>>>> +}
>>>>> +
>>>>> +struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
>>>>> +{
>>>>> + struct ptn3460_bridge *ptn_bridge;
>>>>> +
>>>>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>>>>> +
>>>>> + return ptn_bridge->encoder;
>>>>> +}
>>>>> +
>>>>> +struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
>>>>> + .get_modes = ptn3460_get_modes,
>>>>> + .mode_valid = ptn3460_mode_valid,
>>>>> + .best_encoder = ptn3460_best_encoder,
>>>>> +};
>>>>> +
>>>>> +enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
>>>>> + bool force)
>>>>> +{
>>>>> + return connector_status_connected;
>>>>> +}
>>>>> +
>>>>> +void ptn3460_connector_destroy(struct drm_connector *connector)
>>>>> +{
>>>>> + drm_connector_cleanup(connector);
>>>>> +}
>>>>> +
>>>>> +struct drm_connector_funcs ptn3460_connector_funcs = {
>>>>> + .dpms = drm_helper_connector_dpms,
>>>>> + .fill_modes = drm_helper_probe_single_connector_modes,
>>>>> + .detect = ptn3460_detect,
>>>>> + .destroy = ptn3460_connector_destroy,
>>>>> +};
>>>>
>>>> Why do you try to add a new connector here? We already have the
>>>> connector for LCD, and also provides some callbacks for it. For this,
>>>> please see exynos_drm_display_ops of exynos_drm_fimd driver, and you
>>>> can add new callbacks to there such as init callback for bridge device
>>>> initialization if needed.
>>>>
>>>
>>> We add a new connector for 2 reasons:
>>>
>>> 1) We need to override the drm detect() callback to always return true
>>> since the DP driver will presumably return its hotplug status which
>>> will always be low when the ptn chip is turned off.
>>> 2) We want the ability to control the result of get_modes().
>>>
>>> I've got a patch set almost ready to tear the display ops out of fimd
>>> and put them in the DP driver.
>>
>> Really? if so, that is ideal something we want and we should go. But
>> isn't the DP driver placed in drivers/video/exynos? How did you take
>> care of that?
>
> git mv :)
>

:)


>> Actually, for this, we planned to use CDF(Common Display
>> Framework) if the framework is merged to mainline somehow.
>>
>
> Right. I think CDF will end up being a series of improvements to drm,
> as opposed to its own framework (at least this was the conclusion I
> came to after speaking with Laurent at the plumbers conference). I
> don't think it makes sense to have the DP driver outside of drm. The
> HDMI driver is already inside drm, the DP driver should be too.

See the below,

Application
--------------------------------------------------------------
v4l2 drm kms
hdmi driver hdmi driver
-------------------------------------------------------------
hdmi hw

HDMI is a controller can be controlled by user application, and for
this some frameworks such as v4l2 and drm kms interfaces are used. But
DP, MIPI-DSI, LVDS, and so on aren't controlled by user application.
These are just display bus between scanout devices(hdmi, fimd) and lcd
panel. So the above your example doesn't make sense.

> Regardless, this conversation is only tangentially related to this
> patch and can probably be deferred.
>
> Sean
>
>>> The display ops are better suited
>>> there, since it's the actual encoder/connector. I hope to get that
>>> posted this week.
>>>
>>
>> I will look forward to that posting. :)
>>
>>>
>>>>> +
>>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>>> + struct i2c_client *client, struct device_node *node)
>>>>> +{
>>>>> + int ret;
>>>>> + struct drm_bridge *bridge;
>>>>> + struct ptn3460_bridge *ptn_bridge;
>>>>> +
>>>>> + bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
>>>>> + if (!bridge) {
>>>>> + DRM_ERROR("Failed to allocate drm bridge\n");
>>>>> + return -ENOMEM;
>>>>> + }
>>>>> +
>>>>> + ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
>>>>> + if (!ptn_bridge) {
>>>>> + DRM_ERROR("Failed to allocate ptn bridge\n");
>>>>> + return -ENOMEM;
>>>>> + }
>>>>> +
>>>>> + ptn_bridge->client = client;
>>>>> + ptn_bridge->encoder = encoder;
>>>>> + ptn_bridge->bridge = bridge;
>>>>> + ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
>>>>
>>>> Also, if a regulator is used instead?
>>>>
>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
>>>>> + ret = gpio_request_one(ptn_bridge->gpio_pd_n,
>>>>> + GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
>>>>> + if (ret) {
>>>>> + DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>>>>> + /*
>>>>> + * Request the reset pin low to avoid the bridge being
>>>>> + * initialized prematurely
>>>>> + */
>>>>> + ret = gpio_request_one(ptn_bridge->gpio_rst_n,
>>>>> + GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
>>>>> + if (ret) {
>>>>> + DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
>>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>>> + return ret;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + ret = of_property_read_u32(node, "edid-emulation",
>>>>> + &ptn_bridge->edid_emulation);
>>>>> + if (ret) {
>>>>> + DRM_ERROR("Can't read edid emulation value\n");
>>>>> + goto err;
>>>>> + }
>>>>> +
>>>>> + ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
>>>>> + if (ret) {
>>>>> + DRM_ERROR("Failed to initialize bridge with drm\n");
>>>>> + goto err;
>>>>> + }
>>>>> +
>>>>> + bridge->driver_private = ptn_bridge;
>>>>> + encoder->bridge = bridge;
>>>>> +
>>>>> + ret = drm_connector_init(dev, &ptn_bridge->connector,
>>>>> + &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
>>>>
>>>> So it seems that here's not right place to call drm_connector_init function.
>>>>
>>>> Display pipeline path could be one of,
>>>> Display Controller Display bus
>>>> ---------------------------------------------------------------------------
>>>> FIMD---------------------LVDS--------------------LCD,
>>>> or
>>>> FIMD----------------------eDP---------------------LCD,
>>>> or
>>>> FIMD------------------MIPI-DSI------------------LCD,
>>>> or
>>>> FIMD-------------------------------------------------LCD
>>>>
>>>> And also in case using image enhancement chip,
>>>> mDNIe-------------FIMD-LITE between Display Controller and Display
>>>> bus.
>>>>
>>>> So, wouldn't the right place below FIMD driver? :)
>>>>
>>>
>>> Well, this driver should be considered outside of exynos context since
>>> it could be used by any drm driver.
>>>
>>> In the exynos context, the right place to implement it would be in the
>>> dp driver, actually. However, the exynos driver has a level of
>>> abstraction on top of the crtcs/encoders such that we need to
>>> initialize it in the exynos_drm_core. The patchset I mentioned above
>>> should help move things in a direction where fimd/mixer implement
>>> drm_crtc directly and hdmi/dp implement drm_encoder/drm_connector
>>> directly. In that world, DP would initialize the ptn driver.
>>>
>>>>
>>>>> + if (ret) {
>>>>> + DRM_ERROR("Failed to initialize connector with drm\n");
>>>>> + goto err;
>>>>> + }
>>>>> + drm_connector_helper_add(&ptn_bridge->connector,
>>>>> + &ptn3460_connector_helper_funcs);
>>>>> + drm_sysfs_connector_add(&ptn_bridge->connector);
>>>>> + drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
>>>>> +
>>>>> + return 0;
>>>>> +
>>>>> +err:
>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>>> + gpio_free(ptn_bridge->gpio_rst_n);
>>>>> + return ret;
>>>>> +}
>>>>> diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
>>>>> new file mode 100644
>>>>> index 0000000..157ffa1
>>>>> --- /dev/null
>>>>> +++ b/include/drm/bridge/ptn3460.h
>>>>> @@ -0,0 +1,36 @@
>>>>> +/*
>>>>> + * Copyright (C) 2013 Google, Inc.
>>>>> + *
>>>>> + * 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 _DRM_BRIDGE_PTN3460_H_
>>>>> +#define _DRM_BRIDGE_PTN3460_H_
>>>>> +
>>>>> +struct drm_device;
>>>>> +struct drm_encoder;
>>>>> +struct i2c_client;
>>>>> +struct device_node;
>>>>> +
>>>>> +#ifdef CONFIG_DRM_PTN3460
>>>>> +
>>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>>> + struct i2c_client *client, struct device_node *node);
>>>>> +#else
>>>>> +
>>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>>> + struct i2c_client *client, struct device_node *node)
>>>>> +{
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +#endif
>>>>> +
>>>>> +#endif
>>>>> --
>>>>> 1.8.4
>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> linux-arm-kernel mailing list
>>>>> [email protected]
>>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>> _______________________________________________
>>> dri-devel mailing list
>>> [email protected]
>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> _______________________________________________
> dri-devel mailing list
> [email protected]
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

2013-10-03 18:32:36

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

On Thu, Oct 3, 2013 at 2:23 PM, Inki Dae <[email protected]> wrote:
> 2013/10/4 Sean Paul <[email protected]>:
>> On Thu, Oct 3, 2013 at 1:39 PM, Inki Dae <[email protected]> wrote:
>>> 2013/10/3 Sean Paul <[email protected]>:
>>>> On Thu, Oct 3, 2013 at 9:55 AM, Inki Dae <[email protected]> wrote:
>>>>> Hi, thank you for your contribution and the below is my short comments,
>>>>>
>>>>> 2013/10/2 Sean Paul <[email protected]>:
>>>>>> This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
>>>>>> bridge chip.
>>>>>>
>>>>>> Signed-off-by: Sean Paul <[email protected]>
>>>>>> ---
>>>>>> .../devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++
>>>>>> drivers/gpu/drm/Kconfig | 2 +
>>>>>> drivers/gpu/drm/Makefile | 1 +
>>>>>> drivers/gpu/drm/bridge/Kconfig | 4 +
>>>>>> drivers/gpu/drm/bridge/Makefile | 3 +
>>>>>> drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++
>>>>>> include/drm/bridge/ptn3460.h | 36 +++
>>>>>> 7 files changed, 422 insertions(+)
>>>>>> create mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>>>> create mode 100644 drivers/gpu/drm/bridge/Kconfig
>>>>>> create mode 100644 drivers/gpu/drm/bridge/Makefile
>>>>>> create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
>>>>>> create mode 100644 include/drm/bridge/ptn3460.h
>>>>>>
>>>>>> diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>>>> new file mode 100644
>>>>>> index 0000000..c1cd329
>>>>>> --- /dev/null
>>>>>> +++ b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>>>> @@ -0,0 +1,27 @@
>>>>>> +ptn3460-bridge bindings
>>>>>> +
>>>>>> +Required properties:
>>>>>> + - compatible: "nxp,ptn3460"
>>>>>> + - reg: i2c address of the bridge
>>>>>> + - powerdown-gpio: OF device-tree gpio specification
>>>>>
>>>>> Can a regulator be used instead of gpio in other board case?
>>>>>
>>>>
>>>> No, not to my knowledge.
>>>>
>>>
>>> Hm.. plz check it out again. the gpio pin is specific to board, and
>>> the the gpio be used as power source trigger could be replaced with a
>>> regulator according to board design. So you should consider all
>>> possibilities even though there are no other cases yet: other board
>>> could use a regulator instead.
>>>
>>>>
>>>>>> + - reset-gpio: OF device-tree gpio specification
>>>>>> + - edid-emulation: The EDID emulation entry to use
>>>>>> + +-------+------------+------------------+
>>>>>> + | Value | Resolution | Description |
>>>>>> + | 0 | 1024x768 | NXP Generic |
>>>>>> + | 1 | 1920x1080 | NXP Generic |
>>>>>> + | 2 | 1920x1080 | NXP Generic |
>>>>>> + | 3 | 1600x900 | Samsung LTM200KT |
>>>>>> + | 4 | 1920x1080 | Samsung LTM230HT |
>>>>>> + | 5 | 1366x768 | NXP Generic |
>>>>>> + | 6 | 1600x900 | ChiMei M215HGE |
>>>>>> + +-------+------------+------------------+
>>>>>> +
>>>>>> +Example:
>>>>>> + ptn3460-bridge@20 {
>>>>>> + compatible = "nxp,ptn3460";
>>>>>> + reg = <0x20>;
>>>>>> + powerdown-gpio = <&gpy2 5 1 0 0>;
>>>>>> + reset-gpio = <&gpx1 5 1 0 0>;
>>>>>> + edid-emulation = <5>;
>>>>>> + };
>>>>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>>>>> index 955555d..cd7bfb3 100644
>>>>>> --- a/drivers/gpu/drm/Kconfig
>>>>>> +++ b/drivers/gpu/drm/Kconfig
>>>>>> @@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
>>>>>> source "drivers/gpu/drm/qxl/Kconfig"
>>>>>>
>>>>>> source "drivers/gpu/drm/msm/Kconfig"
>>>>>> +
>>>>>> +source "drivers/gpu/drm/bridge/Kconfig"
>>>>>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>>>>>> index f089adf..9234253 100644
>>>>>> --- a/drivers/gpu/drm/Makefile
>>>>>> +++ b/drivers/gpu/drm/Makefile
>>>>>> @@ -56,3 +56,4 @@ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
>>>>>> obj-$(CONFIG_DRM_QXL) += qxl/
>>>>>> obj-$(CONFIG_DRM_MSM) += msm/
>>>>>> obj-y += i2c/
>>>>>> +obj-y += bridge/
>>>>>> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
>>>>>> new file mode 100644
>>>>>> index 0000000..f8db069
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/gpu/drm/bridge/Kconfig
>>>>>> @@ -0,0 +1,4 @@
>>>>>> +config DRM_PTN3460
>>>>>> + tristate "PTN3460 DP/LVDS bridge"
>>>>>> + depends on DRM && I2C
>>>>>> + ---help---
>>>>>> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
>>>>>> new file mode 100644
>>>>>> index 0000000..b4733e1
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/gpu/drm/bridge/Makefile
>>>>>> @@ -0,0 +1,3 @@
>>>>>> +ccflags-y := -Iinclude/drm
>>>>>> +
>>>>>> +obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>>>>>> diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
>>>>>> new file mode 100644
>>>>>> index 0000000..a9e5c1a
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/gpu/drm/bridge/ptn3460.c
>>>>>> @@ -0,0 +1,349 @@
>>>>>> +/*
>>>>>> + * NXP PTN3460 DP/LVDS bridge driver
>>>>>> + *
>>>>>> + * Copyright (C) 2013 Google, Inc.
>>>>>> + *
>>>>>> + * 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/of.h>
>>>>>> +#include <linux/of_gpio.h>
>>>>>> +#include <linux/i2c.h>
>>>>>> +#include <linux/gpio.h>
>>>>>> +#include <linux/delay.h>
>>>>>> +
>>>>>> +#include "drmP.h"
>>>>>> +#include "drm_edid.h"
>>>>>> +#include "drm_crtc.h"
>>>>>> +#include "drm_crtc_helper.h"
>>>>>> +
>>>>>> +#include "bridge/ptn3460.h"
>>>>>> +
>>>>>> +#define PTN3460_EDID_ADDR 0x0
>>>>>> +#define PTN3460_EDID_EMULATION_ADDR 0x84
>>>>>> +#define PTN3460_EDID_ENABLE_EMULATION 0
>>>>>> +#define PTN3460_EDID_EMULATION_SELECTION 1
>>>>>> +#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
>>>>>> +
>>>>>> +struct ptn3460_bridge {
>>>>>> + struct drm_connector connector;
>>>>>> + struct i2c_client *client;
>>>>>> + struct drm_encoder *encoder;
>>>>>> + struct drm_bridge *bridge;
>>>>>> + struct edid *edid;
>>>>>> + int gpio_pd_n;
>>>>>> + int gpio_rst_n;
>>>>>> + u32 edid_emulation;
>>>>>> + bool enabled;
>>>>>> +};
>>>>>> +
>>>>>> +static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
>>>>>> + u8 *buf, int len)
>>>>>> +{
>>>>>> + int ret;
>>>>>> +
>>>>>> + ret = i2c_master_send(ptn_bridge->client, &addr, 1);
>>>>>> + if (ret <= 0) {
>>>>>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + ret = i2c_master_recv(ptn_bridge->client, buf, len);
>>>>>> + if (ret <= 0) {
>>>>>> + DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
>>>>>> + char val)
>>>>>> +{
>>>>>> + int ret;
>>>>>> + char buf[2];
>>>>>> +
>>>>>> + buf[0] = addr;
>>>>>> + buf[1] = val;
>>>>>> +
>>>>>> + ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
>>>>>> + if (ret <= 0) {
>>>>>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
>>>>>> +{
>>>>>> + int ret;
>>>>>> + char val;
>>>>>> +
>>>>>> + /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
>>>>>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
>>>>>> + ptn_bridge->edid_emulation);
>>>>>> + if (ret) {
>>>>>> + DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + /* Enable EDID emulation and select the desired EDID */
>>>>>> + val = 1 << PTN3460_EDID_ENABLE_EMULATION |
>>>>>> + ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
>>>>>> +
>>>>>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
>>>>>> + if (ret) {
>>>>>> + DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static void ptn3460_pre_enable(struct drm_bridge *bridge)
>>>>>> +{
>>>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>>>> + int ret;
>>>>>> +
>>>>>> + if (ptn_bridge->enabled)
>>>>>> + return;
>>>>>> +
>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>>> + gpio_set_value(ptn_bridge->gpio_pd_n, 1);
>>>>>
>>>>> Ditto.
>>>>>
>>>>>> +
>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>>>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 0);
>>>>>> + udelay(10);
>>>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>>>>> + }
>>>>>> +
>>>>>> + /*
>>>>>> + * There's a bug in the PTN chip where it falsely asserts hotplug before
>>>>>> + * it is fully functional. We're forced to wait for the maximum start up
>>>>>> + * time specified in the chip's datasheet to make sure we're really up.
>>>>>> + */
>>>>>> + msleep(90);
>>>>>> +
>>>>>> + ret = ptn3460_select_edid(ptn_bridge);
>>>>>> + if (ret)
>>>>>> + DRM_ERROR("Select edid failed ret=%d\n", ret);
>>>>>> +
>>>>>> + ptn_bridge->enabled = true;
>>>>>> +}
>>>>>> +
>>>>>> +static void ptn3460_enable(struct drm_bridge *bridge)
>>>>>> +{
>>>>>> +}
>>>>>> +
>>>>>> +static void ptn3460_disable(struct drm_bridge *bridge)
>>>>>> +{
>>>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>>>> +
>>>>>> + if (!ptn_bridge->enabled)
>>>>>> + return;
>>>>>> +
>>>>>> + ptn_bridge->enabled = false;
>>>>>> +
>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>>>>> +
>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>>> + gpio_set_value(ptn_bridge->gpio_pd_n, 0);
>>>>>
>>>>> Ditto.
>>>>>
>>>>>> +}
>>>>>> +
>>>>>> +static void ptn3460_post_disable(struct drm_bridge *bridge)
>>>>>> +{
>>>>>> +}
>>>>>> +
>>>>>> +void ptn3460_bridge_destroy(struct drm_bridge *bridge)
>>>>>> +{
>>>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>>>> +
>>>>>> + drm_bridge_cleanup(bridge);
>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>>>
>>>>> Ditto.
>>>>>
>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>>>> + gpio_free(ptn_bridge->gpio_rst_n);
>>>>>> + /* Nothing else to free, we've got devm allocated memory */
>>>>>> +}
>>>>>> +
>>>>>> +struct drm_bridge_funcs ptn3460_bridge_funcs = {
>>>>>> + .pre_enable = ptn3460_pre_enable,
>>>>>> + .enable = ptn3460_enable,
>>>>>> + .disable = ptn3460_disable,
>>>>>> + .post_disable = ptn3460_post_disable,
>>>>>> + .destroy = ptn3460_bridge_destroy,
>>>>>> +};
>>>>>> +
>>>>>> +int ptn3460_get_modes(struct drm_connector *connector)
>>>>>> +{
>>>>>> + struct ptn3460_bridge *ptn_bridge;
>>>>>> + u8 *edid;
>>>>>> + int ret, num_modes;
>>>>>> + bool power_off;
>>>>>> +
>>>>>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>>>>>> +
>>>>>> + if (ptn_bridge->edid)
>>>>>> + return drm_add_edid_modes(connector, ptn_bridge->edid);
>>>>>> +
>>>>>> + power_off = !ptn_bridge->enabled;
>>>>>> + ptn3460_pre_enable(ptn_bridge->bridge);
>>>>>> +
>>>>>> + edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
>>>>>> + if (!edid) {
>>>>>> + DRM_ERROR("Failed to allocate edid\n");
>>>>>> + return 0;
>>>>>> + }
>>>>>> +
>>>>>> + ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
>>>>>> + EDID_LENGTH);
>>>>>> + if (ret) {
>>>>>> + kfree(edid);
>>>>>> + num_modes = 0;
>>>>>> + goto out;
>>>>>> + }
>>>>>> +
>>>>>> + ptn_bridge->edid = (struct edid *)edid;
>>>>>> + drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
>>>>>> +
>>>>>> + num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
>>>>>> +
>>>>>> +out:
>>>>>> + if (power_off)
>>>>>> + ptn3460_disable(ptn_bridge->bridge);
>>>>>> +
>>>>>> + return num_modes;
>>>>>> +}
>>>>>> +
>>>>>> +static int ptn3460_mode_valid(struct drm_connector *connector,
>>>>>> + struct drm_display_mode *mode)
>>>>>> +{
>>>>>> + return MODE_OK;
>>>>>> +}
>>>>>> +
>>>>>> +struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
>>>>>> +{
>>>>>> + struct ptn3460_bridge *ptn_bridge;
>>>>>> +
>>>>>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>>>>>> +
>>>>>> + return ptn_bridge->encoder;
>>>>>> +}
>>>>>> +
>>>>>> +struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
>>>>>> + .get_modes = ptn3460_get_modes,
>>>>>> + .mode_valid = ptn3460_mode_valid,
>>>>>> + .best_encoder = ptn3460_best_encoder,
>>>>>> +};
>>>>>> +
>>>>>> +enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
>>>>>> + bool force)
>>>>>> +{
>>>>>> + return connector_status_connected;
>>>>>> +}
>>>>>> +
>>>>>> +void ptn3460_connector_destroy(struct drm_connector *connector)
>>>>>> +{
>>>>>> + drm_connector_cleanup(connector);
>>>>>> +}
>>>>>> +
>>>>>> +struct drm_connector_funcs ptn3460_connector_funcs = {
>>>>>> + .dpms = drm_helper_connector_dpms,
>>>>>> + .fill_modes = drm_helper_probe_single_connector_modes,
>>>>>> + .detect = ptn3460_detect,
>>>>>> + .destroy = ptn3460_connector_destroy,
>>>>>> +};
>>>>>
>>>>> Why do you try to add a new connector here? We already have the
>>>>> connector for LCD, and also provides some callbacks for it. For this,
>>>>> please see exynos_drm_display_ops of exynos_drm_fimd driver, and you
>>>>> can add new callbacks to there such as init callback for bridge device
>>>>> initialization if needed.
>>>>>
>>>>
>>>> We add a new connector for 2 reasons:
>>>>
>>>> 1) We need to override the drm detect() callback to always return true
>>>> since the DP driver will presumably return its hotplug status which
>>>> will always be low when the ptn chip is turned off.
>>>> 2) We want the ability to control the result of get_modes().
>>>>
>>>> I've got a patch set almost ready to tear the display ops out of fimd
>>>> and put them in the DP driver.
>>>
>>> Really? if so, that is ideal something we want and we should go. But
>>> isn't the DP driver placed in drivers/video/exynos? How did you take
>>> care of that?
>>
>> git mv :)
>>
>
> :)
>
>
>>> Actually, for this, we planned to use CDF(Common Display
>>> Framework) if the framework is merged to mainline somehow.
>>>
>>
>> Right. I think CDF will end up being a series of improvements to drm,
>> as opposed to its own framework (at least this was the conclusion I
>> came to after speaking with Laurent at the plumbers conference). I
>> don't think it makes sense to have the DP driver outside of drm. The
>> HDMI driver is already inside drm, the DP driver should be too.
>
> See the below,
>
> Application
> --------------------------------------------------------------
> v4l2 drm kms
> hdmi driver hdmi driver
> -------------------------------------------------------------
> hdmi hw
>
> HDMI is a controller can be controlled by user application, and for
> this some frameworks such as v4l2 and drm kms interfaces are used. But
> DP, MIPI-DSI, LVDS, and so on aren't controlled by user application.
> These are just display bus between scanout devices(hdmi, fimd) and lcd
> panel. So the above your example doesn't make sense.
>

I think we've probably gone far enough off-topic. Let's discuss this
when you've had an opportunity to see the patchset. I hope the code
will speak for itself.

Aside from Olof's suggestion about changing the dts binding to
lvds-bridge (which I'll upload a new patch for), do you have any
suggestions for this patch?

Sean


>> Regardless, this conversation is only tangentially related to this
>> patch and can probably be deferred.
>>
>> Sean
>>
>>>> The display ops are better suited
>>>> there, since it's the actual encoder/connector. I hope to get that
>>>> posted this week.
>>>>
>>>
>>> I will look forward to that posting. :)
>>>
>>>>
>>>>>> +
>>>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>>>> + struct i2c_client *client, struct device_node *node)
>>>>>> +{
>>>>>> + int ret;
>>>>>> + struct drm_bridge *bridge;
>>>>>> + struct ptn3460_bridge *ptn_bridge;
>>>>>> +
>>>>>> + bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
>>>>>> + if (!bridge) {
>>>>>> + DRM_ERROR("Failed to allocate drm bridge\n");
>>>>>> + return -ENOMEM;
>>>>>> + }
>>>>>> +
>>>>>> + ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
>>>>>> + if (!ptn_bridge) {
>>>>>> + DRM_ERROR("Failed to allocate ptn bridge\n");
>>>>>> + return -ENOMEM;
>>>>>> + }
>>>>>> +
>>>>>> + ptn_bridge->client = client;
>>>>>> + ptn_bridge->encoder = encoder;
>>>>>> + ptn_bridge->bridge = bridge;
>>>>>> + ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
>>>>>
>>>>> Also, if a regulator is used instead?
>>>>>
>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
>>>>>> + ret = gpio_request_one(ptn_bridge->gpio_pd_n,
>>>>>> + GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
>>>>>> + if (ret) {
>>>>>> + DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>> + }
>>>>>> +
>>>>>> + ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>>>>>> + /*
>>>>>> + * Request the reset pin low to avoid the bridge being
>>>>>> + * initialized prematurely
>>>>>> + */
>>>>>> + ret = gpio_request_one(ptn_bridge->gpio_rst_n,
>>>>>> + GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
>>>>>> + if (ret) {
>>>>>> + DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
>>>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>>>> + return ret;
>>>>>> + }
>>>>>> + }
>>>>>> +
>>>>>> + ret = of_property_read_u32(node, "edid-emulation",
>>>>>> + &ptn_bridge->edid_emulation);
>>>>>> + if (ret) {
>>>>>> + DRM_ERROR("Can't read edid emulation value\n");
>>>>>> + goto err;
>>>>>> + }
>>>>>> +
>>>>>> + ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
>>>>>> + if (ret) {
>>>>>> + DRM_ERROR("Failed to initialize bridge with drm\n");
>>>>>> + goto err;
>>>>>> + }
>>>>>> +
>>>>>> + bridge->driver_private = ptn_bridge;
>>>>>> + encoder->bridge = bridge;
>>>>>> +
>>>>>> + ret = drm_connector_init(dev, &ptn_bridge->connector,
>>>>>> + &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
>>>>>
>>>>> So it seems that here's not right place to call drm_connector_init function.
>>>>>
>>>>> Display pipeline path could be one of,
>>>>> Display Controller Display bus
>>>>> ---------------------------------------------------------------------------
>>>>> FIMD---------------------LVDS--------------------LCD,
>>>>> or
>>>>> FIMD----------------------eDP---------------------LCD,
>>>>> or
>>>>> FIMD------------------MIPI-DSI------------------LCD,
>>>>> or
>>>>> FIMD-------------------------------------------------LCD
>>>>>
>>>>> And also in case using image enhancement chip,
>>>>> mDNIe-------------FIMD-LITE between Display Controller and Display
>>>>> bus.
>>>>>
>>>>> So, wouldn't the right place below FIMD driver? :)
>>>>>
>>>>
>>>> Well, this driver should be considered outside of exynos context since
>>>> it could be used by any drm driver.
>>>>
>>>> In the exynos context, the right place to implement it would be in the
>>>> dp driver, actually. However, the exynos driver has a level of
>>>> abstraction on top of the crtcs/encoders such that we need to
>>>> initialize it in the exynos_drm_core. The patchset I mentioned above
>>>> should help move things in a direction where fimd/mixer implement
>>>> drm_crtc directly and hdmi/dp implement drm_encoder/drm_connector
>>>> directly. In that world, DP would initialize the ptn driver.
>>>>
>>>>>
>>>>>> + if (ret) {
>>>>>> + DRM_ERROR("Failed to initialize connector with drm\n");
>>>>>> + goto err;
>>>>>> + }
>>>>>> + drm_connector_helper_add(&ptn_bridge->connector,
>>>>>> + &ptn3460_connector_helper_funcs);
>>>>>> + drm_sysfs_connector_add(&ptn_bridge->connector);
>>>>>> + drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
>>>>>> +
>>>>>> + return 0;
>>>>>> +
>>>>>> +err:
>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>>>> + gpio_free(ptn_bridge->gpio_rst_n);
>>>>>> + return ret;
>>>>>> +}
>>>>>> diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
>>>>>> new file mode 100644
>>>>>> index 0000000..157ffa1
>>>>>> --- /dev/null
>>>>>> +++ b/include/drm/bridge/ptn3460.h
>>>>>> @@ -0,0 +1,36 @@
>>>>>> +/*
>>>>>> + * Copyright (C) 2013 Google, Inc.
>>>>>> + *
>>>>>> + * 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 _DRM_BRIDGE_PTN3460_H_
>>>>>> +#define _DRM_BRIDGE_PTN3460_H_
>>>>>> +
>>>>>> +struct drm_device;
>>>>>> +struct drm_encoder;
>>>>>> +struct i2c_client;
>>>>>> +struct device_node;
>>>>>> +
>>>>>> +#ifdef CONFIG_DRM_PTN3460
>>>>>> +
>>>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>>>> + struct i2c_client *client, struct device_node *node);
>>>>>> +#else
>>>>>> +
>>>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>>>> + struct i2c_client *client, struct device_node *node)
>>>>>> +{
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> +#endif
>>>>>> +
>>>>>> +#endif
>>>>>> --
>>>>>> 1.8.4
>>>>>>
>>>>>>
>>>>>> _______________________________________________
>>>>>> linux-arm-kernel mailing list
>>>>>> [email protected]
>>>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> [email protected]
>>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>> _______________________________________________
>> dri-devel mailing list
>> [email protected]
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

2013-10-03 22:28:38

by Sean Paul

[permalink] [raw]
Subject: [PATCH v2 0/5] Add some missing bits for exynos5250-snow

This set adds some missing devicetree nodes to the exynos5250-snow file as well
as adds a drm_bridge driver for the ptn3460 DP-LVDS chip. This chip is used in
the exynos5250-snow board.

Sean


Sean Paul (5):
ARM: dts: Add fimd display-timings for exynos5250-snow
ARM: dts: Add dp-controller node to exynos5250-snow
drm/bridge: Add PTN3460 bridge driver
drm/exynos: Initialize ptn3460 if present
ARM: dts: Add ptn3460 to exynos5250-snow

Documentation/devicetree/bindings/drm/bridge/ptn3460.txt | 27 +++
arch/arm/boot/dts/exynos5250-snow.dts | 48 +++++
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/bridge/Kconfig | 4 +
drivers/gpu/drm/bridge/Makefile | 3 +
drivers/gpu/drm/bridge/ptn3460.c | 349 ++++++++++++++++
drivers/gpu/drm/exynos/exynos_drm_core.c | 44 ++++-
include/drm/bridge/ptn3460.h | 37 ++++
9 files changed, 514 insertions(+), 1 deletion(-)

2013-10-03 22:28:46

by Sean Paul

[permalink] [raw]
Subject: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present

This patch adds code to look for the ptn3460 in the device tree file on
exynos initialization. If ptn node is found, the driver will initialize
the ptn3460 driver and skip creating a DP connector (since the bridge
driver will register its own connector).

Signed-off-by: Sean Paul <[email protected]>
---

v2:
- Changed include from of_i2c.h to i2c.h
- Changed of_find_by_name to of_find_compatible

drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 1bef6dc..08ca4f9 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -12,7 +12,9 @@
* option) any later version.
*/

+#include <linux/i2c.h>
#include <drm/drmP.h>
+#include <drm/bridge/ptn3460.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_connector.h"
@@ -20,6 +22,40 @@

static LIST_HEAD(exynos_drm_subdrv_list);

+struct bridge_init {
+ struct i2c_client *client;
+ struct device_node *node;
+};
+
+static bool find_bridge(const char *compat, struct bridge_init *bridge)
+{
+ bridge->client = NULL;
+ bridge->node = of_find_compatible_node(NULL, NULL, compat);
+ if (!bridge->node)
+ return false;
+
+ bridge->client = of_find_i2c_device_by_node(bridge->node);
+ if (!bridge->client)
+ return false;
+
+ return true;
+}
+
+/* returns the number of bridges attached */
+static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
+ struct drm_encoder *encoder)
+{
+ struct bridge_init bridge;
+ int ret;
+
+ if (find_bridge("nxp,ptn3460", &bridge)) {
+ ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
+ if (!ret)
+ return 1;
+ }
+ return 0;
+}
+
static int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
@@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
DRM_ERROR("failed to create encoder\n");
return -EFAULT;
}
+ subdrv->encoder = encoder;
+
+ if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
+ ret = exynos_drm_attach_lcd_bridge(dev, encoder);
+ if (ret)
+ return 0;
+ }

/*
* create and initialize a connector for this sub driver and
@@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
goto err_destroy_encoder;
}

- subdrv->encoder = encoder;
subdrv->connector = connector;

return 0;
--
1.8.4

2013-10-03 22:28:44

by Sean Paul

[permalink] [raw]
Subject: [PATCH v2 3/5] drm/bridge: Add PTN3460 bridge driver

This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
bridge chip.

Signed-off-by: Sean Paul <[email protected]>
---

v2:
- Changed header definition to static inline
- Changed dt node name to lvds-bridge

.../devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/bridge/Kconfig | 4 +
drivers/gpu/drm/bridge/Makefile | 3 +
drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++
include/drm/bridge/ptn3460.h | 37 +++
7 files changed, 423 insertions(+)
create mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
create mode 100644 drivers/gpu/drm/bridge/Kconfig
create mode 100644 drivers/gpu/drm/bridge/Makefile
create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
create mode 100644 include/drm/bridge/ptn3460.h

diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
new file mode 100644
index 0000000..52b93b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
@@ -0,0 +1,27 @@
+ptn3460 bridge bindings
+
+Required properties:
+ - compatible: "nxp,ptn3460"
+ - reg: i2c address of the bridge
+ - powerdown-gpio: OF device-tree gpio specification
+ - reset-gpio: OF device-tree gpio specification
+ - edid-emulation: The EDID emulation entry to use
+ +-------+------------+------------------+
+ | Value | Resolution | Description |
+ | 0 | 1024x768 | NXP Generic |
+ | 1 | 1920x1080 | NXP Generic |
+ | 2 | 1920x1080 | NXP Generic |
+ | 3 | 1600x900 | Samsung LTM200KT |
+ | 4 | 1920x1080 | Samsung LTM230HT |
+ | 5 | 1366x768 | NXP Generic |
+ | 6 | 1600x900 | ChiMei M215HGE |
+ +-------+------------+------------------+
+
+Example:
+ lvds-bridge@20 {
+ compatible = "nxp,ptn3460";
+ reg = <0x20>;
+ powerdown-gpio = <&gpy2 5 1 0 0>;
+ reset-gpio = <&gpx1 5 1 0 0>;
+ edid-emulation = <5>;
+ };
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 955555d..cd7bfb3 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
source "drivers/gpu/drm/qxl/Kconfig"

source "drivers/gpu/drm/msm/Kconfig"
+
+source "drivers/gpu/drm/bridge/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index f089adf..9234253 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -56,3 +56,4 @@ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_MSM) += msm/
obj-y += i2c/
+obj-y += bridge/
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
new file mode 100644
index 0000000..f8db069
--- /dev/null
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -0,0 +1,4 @@
+config DRM_PTN3460
+ tristate "PTN3460 DP/LVDS bridge"
+ depends on DRM && I2C
+ ---help---
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
new file mode 100644
index 0000000..b4733e1
--- /dev/null
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -0,0 +1,3 @@
+ccflags-y := -Iinclude/drm
+
+obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
new file mode 100644
index 0000000..a9e5c1a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ptn3460.c
@@ -0,0 +1,349 @@
+/*
+ * NXP PTN3460 DP/LVDS bridge driver
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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/of.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "drmP.h"
+#include "drm_edid.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+#include "bridge/ptn3460.h"
+
+#define PTN3460_EDID_ADDR 0x0
+#define PTN3460_EDID_EMULATION_ADDR 0x84
+#define PTN3460_EDID_ENABLE_EMULATION 0
+#define PTN3460_EDID_EMULATION_SELECTION 1
+#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
+
+struct ptn3460_bridge {
+ struct drm_connector connector;
+ struct i2c_client *client;
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+ struct edid *edid;
+ int gpio_pd_n;
+ int gpio_rst_n;
+ u32 edid_emulation;
+ bool enabled;
+};
+
+static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
+ u8 *buf, int len)
+{
+ int ret;
+
+ ret = i2c_master_send(ptn_bridge->client, &addr, 1);
+ if (ret <= 0) {
+ DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = i2c_master_recv(ptn_bridge->client, buf, len);
+ if (ret <= 0) {
+ DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
+ char val)
+{
+ int ret;
+ char buf[2];
+
+ buf[0] = addr;
+ buf[1] = val;
+
+ ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
+ if (ret <= 0) {
+ DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
+{
+ int ret;
+ char val;
+
+ /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
+ ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
+ ptn_bridge->edid_emulation);
+ if (ret) {
+ DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Enable EDID emulation and select the desired EDID */
+ val = 1 << PTN3460_EDID_ENABLE_EMULATION |
+ ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
+
+ ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
+ if (ret) {
+ DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ptn3460_pre_enable(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+ int ret;
+
+ if (ptn_bridge->enabled)
+ return;
+
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_set_value(ptn_bridge->gpio_pd_n, 1);
+
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
+ gpio_set_value(ptn_bridge->gpio_rst_n, 0);
+ udelay(10);
+ gpio_set_value(ptn_bridge->gpio_rst_n, 1);
+ }
+
+ /*
+ * There's a bug in the PTN chip where it falsely asserts hotplug before
+ * it is fully functional. We're forced to wait for the maximum start up
+ * time specified in the chip's datasheet to make sure we're really up.
+ */
+ msleep(90);
+
+ ret = ptn3460_select_edid(ptn_bridge);
+ if (ret)
+ DRM_ERROR("Select edid failed ret=%d\n", ret);
+
+ ptn_bridge->enabled = true;
+}
+
+static void ptn3460_enable(struct drm_bridge *bridge)
+{
+}
+
+static void ptn3460_disable(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+
+ if (!ptn_bridge->enabled)
+ return;
+
+ ptn_bridge->enabled = false;
+
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_set_value(ptn_bridge->gpio_rst_n, 1);
+
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_set_value(ptn_bridge->gpio_pd_n, 0);
+}
+
+static void ptn3460_post_disable(struct drm_bridge *bridge)
+{
+}
+
+void ptn3460_bridge_destroy(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+
+ drm_bridge_cleanup(bridge);
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_free(ptn_bridge->gpio_pd_n);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_free(ptn_bridge->gpio_rst_n);
+ /* Nothing else to free, we've got devm allocated memory */
+}
+
+struct drm_bridge_funcs ptn3460_bridge_funcs = {
+ .pre_enable = ptn3460_pre_enable,
+ .enable = ptn3460_enable,
+ .disable = ptn3460_disable,
+ .post_disable = ptn3460_post_disable,
+ .destroy = ptn3460_bridge_destroy,
+};
+
+int ptn3460_get_modes(struct drm_connector *connector)
+{
+ struct ptn3460_bridge *ptn_bridge;
+ u8 *edid;
+ int ret, num_modes;
+ bool power_off;
+
+ ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
+
+ if (ptn_bridge->edid)
+ return drm_add_edid_modes(connector, ptn_bridge->edid);
+
+ power_off = !ptn_bridge->enabled;
+ ptn3460_pre_enable(ptn_bridge->bridge);
+
+ edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!edid) {
+ DRM_ERROR("Failed to allocate edid\n");
+ return 0;
+ }
+
+ ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
+ EDID_LENGTH);
+ if (ret) {
+ kfree(edid);
+ num_modes = 0;
+ goto out;
+ }
+
+ ptn_bridge->edid = (struct edid *)edid;
+ drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
+
+ num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
+
+out:
+ if (power_off)
+ ptn3460_disable(ptn_bridge->bridge);
+
+ return num_modes;
+}
+
+static int ptn3460_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
+{
+ struct ptn3460_bridge *ptn_bridge;
+
+ ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
+
+ return ptn_bridge->encoder;
+}
+
+struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
+ .get_modes = ptn3460_get_modes,
+ .mode_valid = ptn3460_mode_valid,
+ .best_encoder = ptn3460_best_encoder,
+};
+
+enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
+ bool force)
+{
+ return connector_status_connected;
+}
+
+void ptn3460_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_cleanup(connector);
+}
+
+struct drm_connector_funcs ptn3460_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = ptn3460_detect,
+ .destroy = ptn3460_connector_destroy,
+};
+
+int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
+ struct i2c_client *client, struct device_node *node)
+{
+ int ret;
+ struct drm_bridge *bridge;
+ struct ptn3460_bridge *ptn_bridge;
+
+ bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
+ if (!bridge) {
+ DRM_ERROR("Failed to allocate drm bridge\n");
+ return -ENOMEM;
+ }
+
+ ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
+ if (!ptn_bridge) {
+ DRM_ERROR("Failed to allocate ptn bridge\n");
+ return -ENOMEM;
+ }
+
+ ptn_bridge->client = client;
+ ptn_bridge->encoder = encoder;
+ ptn_bridge->bridge = bridge;
+ ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
+ ret = gpio_request_one(ptn_bridge->gpio_pd_n,
+ GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
+ if (ret) {
+ DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
+ /*
+ * Request the reset pin low to avoid the bridge being
+ * initialized prematurely
+ */
+ ret = gpio_request_one(ptn_bridge->gpio_rst_n,
+ GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
+ if (ret) {
+ DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
+ gpio_free(ptn_bridge->gpio_pd_n);
+ return ret;
+ }
+ }
+
+ ret = of_property_read_u32(node, "edid-emulation",
+ &ptn_bridge->edid_emulation);
+ if (ret) {
+ DRM_ERROR("Can't read edid emulation value\n");
+ goto err;
+ }
+
+ ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
+ if (ret) {
+ DRM_ERROR("Failed to initialize bridge with drm\n");
+ goto err;
+ }
+
+ bridge->driver_private = ptn_bridge;
+ encoder->bridge = bridge;
+
+ ret = drm_connector_init(dev, &ptn_bridge->connector,
+ &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ goto err;
+ }
+ drm_connector_helper_add(&ptn_bridge->connector,
+ &ptn3460_connector_helper_funcs);
+ drm_sysfs_connector_add(&ptn_bridge->connector);
+ drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
+
+ return 0;
+
+err:
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_free(ptn_bridge->gpio_pd_n);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_free(ptn_bridge->gpio_rst_n);
+ return ret;
+}
diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
new file mode 100644
index 0000000..8481816
--- /dev/null
+++ b/include/drm/bridge/ptn3460.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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 _DRM_BRIDGE_PTN3460_H_
+#define _DRM_BRIDGE_PTN3460_H_
+
+struct drm_device;
+struct drm_encoder;
+struct i2c_client;
+struct device_node;
+
+#ifdef CONFIG_DRM_PTN3460
+
+int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
+ struct i2c_client *client, struct device_node *node);
+#else
+
+static inline int ptn3460_init(struct drm_device *dev,
+ struct drm_encoder *encoder, struct i2c_client *client,
+ struct device_node *node)
+{
+ return 0;
+}
+
+#endif
+
+#endif
--
1.8.4

2013-10-03 22:29:16

by Sean Paul

[permalink] [raw]
Subject: [PATCH v2 5/5] ARM: dts: Add ptn3460 to exynos5250-snow

This patch adds a node for the ptn3460 DP-LVDS chip in the
exynos5250-snow board dts file.

Signed-off-by: Sean Paul <[email protected]>
---

v2: Changed node name to lvds-bridge

arch/arm/boot/dts/exynos5250-snow.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index f1cf68e..2870205 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -33,6 +33,13 @@
sd3_bus4: sd3-bus-width4 {
samsung,pin-drv = <0>;
};
+
+ ptn3460_gpios: ptn3460-gpios {
+ samsung,pins = "gpy2-5", "gpx1-5";
+ samsung,pin-function = <1>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
};

gpio-keys {
@@ -186,6 +193,18 @@
samsung,vbus-gpio = <&gpx1 1 0>;
};

+ i2c@12CD0000 {
+ lvds-bridge@20 {
+ compatible = "nxp,ptn3460";
+ reg = <0x20>;
+ powerdown-gpio = <&gpy2 5 0>;
+ reset-gpio = <&gpx1 5 0>;
+ edid-emulation = <5>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&ptn3460_gpios>;
+ };
+ };
+
dp-controller@145B0000 {
samsung,color-space = <0>;
samsung,dynamic-range = <0>;
--
1.8.4

2013-10-03 22:29:35

by Sean Paul

[permalink] [raw]
Subject: [PATCH v2 2/5] ARM: dts: Add dp-controller node to exynos5250-snow

This patch adds the dp-controller node to the exynos5250-snow board dts
file.

Signed-off-by: Sean Paul <[email protected]>
---

v2: Added dp-controller address to node (rebased on linux-next)

arch/arm/boot/dts/exynos5250-snow.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index 2a0f0de..f1cf68e 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -186,6 +186,18 @@
samsung,vbus-gpio = <&gpx1 1 0>;
};

+ dp-controller@145B0000 {
+ samsung,color-space = <0>;
+ samsung,dynamic-range = <0>;
+ samsung,ycbcr-coeff = <0>;
+ samsung,color-depth = <1>;
+ samsung,link-rate = <0x0a>;
+ samsung,lane-count = <2>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&dp_hpd>;
+ };
+
fimd: fimd@14400000 {
display-timings {
native-mode = <&lcd_timing>;
--
1.8.4

2013-10-03 22:29:54

by Sean Paul

[permalink] [raw]
Subject: [PATCH v2 1/5] ARM: dts: Add fimd display-timings for exynos5250-snow

This patch adds the internal panel timings to the exynos5250-snow board
dts file.

Signed-off-by: Sean Paul <[email protected]>
---

v2: No difference

arch/arm/boot/dts/exynos5250-snow.dts | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index fd711e2..2a0f0de 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -186,6 +186,23 @@
samsung,vbus-gpio = <&gpx1 1 0>;
};

+ fimd: fimd@14400000 {
+ display-timings {
+ native-mode = <&lcd_timing>;
+ lcd_timing: 1366x768 {
+ clock-frequency = <70589280>;
+ hactive = <1366>;
+ vactive = <768>;
+ hfront-porch = <40>;
+ hback-porch = <40>;
+ hsync-len = <32>;
+ vback-porch = <10>;
+ vfront-porch = <12>;
+ vsync-len = <6>;
+ };
+ };
+ };
+
fixed-rate-clocks {
xxti {
compatible = "samsung,clock-xxti";
--
1.8.4

2013-10-03 22:35:55

by Olof Johansson

[permalink] [raw]
Subject: Re: [PATCH v2 0/5] Add some missing bits for exynos5250-snow

On Thu, Oct 3, 2013 at 3:28 PM, Sean Paul <[email protected]> wrote:
> This set adds some missing devicetree nodes to the exynos5250-snow file as well
> as adds a drm_bridge driver for the ptn3460 DP-LVDS chip. This chip is used in
> the exynos5250-snow board.
>
> Sean

Series:

Acked-by: Olof Johansson <[email protected]>


-Olof

Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

2013/10/4 Sean Paul <[email protected]>:
> On Thu, Oct 3, 2013 at 2:23 PM, Inki Dae <[email protected]> wrote:
>> 2013/10/4 Sean Paul <[email protected]>:
>>> On Thu, Oct 3, 2013 at 1:39 PM, Inki Dae <[email protected]> wrote:
>>>> 2013/10/3 Sean Paul <[email protected]>:
>>>>> On Thu, Oct 3, 2013 at 9:55 AM, Inki Dae <[email protected]> wrote:
>>>>>> Hi, thank you for your contribution and the below is my short comments,
>>>>>>
>>>>>> 2013/10/2 Sean Paul <[email protected]>:
>>>>>>> This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
>>>>>>> bridge chip.
>>>>>>>
>>>>>>> Signed-off-by: Sean Paul <[email protected]>
>>>>>>> ---
>>>>>>> .../devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++
>>>>>>> drivers/gpu/drm/Kconfig | 2 +
>>>>>>> drivers/gpu/drm/Makefile | 1 +
>>>>>>> drivers/gpu/drm/bridge/Kconfig | 4 +
>>>>>>> drivers/gpu/drm/bridge/Makefile | 3 +
>>>>>>> drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++
>>>>>>> include/drm/bridge/ptn3460.h | 36 +++
>>>>>>> 7 files changed, 422 insertions(+)
>>>>>>> create mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>>>>> create mode 100644 drivers/gpu/drm/bridge/Kconfig
>>>>>>> create mode 100644 drivers/gpu/drm/bridge/Makefile
>>>>>>> create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
>>>>>>> create mode 100644 include/drm/bridge/ptn3460.h
>>>>>>>
>>>>>>> diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>>>>> new file mode 100644
>>>>>>> index 0000000..c1cd329
>>>>>>> --- /dev/null
>>>>>>> +++ b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
>>>>>>> @@ -0,0 +1,27 @@
>>>>>>> +ptn3460-bridge bindings
>>>>>>> +
>>>>>>> +Required properties:
>>>>>>> + - compatible: "nxp,ptn3460"
>>>>>>> + - reg: i2c address of the bridge
>>>>>>> + - powerdown-gpio: OF device-tree gpio specification
>>>>>>
>>>>>> Can a regulator be used instead of gpio in other board case?
>>>>>>
>>>>>
>>>>> No, not to my knowledge.
>>>>>
>>>>
>>>> Hm.. plz check it out again. the gpio pin is specific to board, and
>>>> the the gpio be used as power source trigger could be replaced with a
>>>> regulator according to board design. So you should consider all
>>>> possibilities even though there are no other cases yet: other board
>>>> could use a regulator instead.
>>>>
>>>>>
>>>>>>> + - reset-gpio: OF device-tree gpio specification
>>>>>>> + - edid-emulation: The EDID emulation entry to use
>>>>>>> + +-------+------------+------------------+
>>>>>>> + | Value | Resolution | Description |
>>>>>>> + | 0 | 1024x768 | NXP Generic |
>>>>>>> + | 1 | 1920x1080 | NXP Generic |
>>>>>>> + | 2 | 1920x1080 | NXP Generic |
>>>>>>> + | 3 | 1600x900 | Samsung LTM200KT |
>>>>>>> + | 4 | 1920x1080 | Samsung LTM230HT |
>>>>>>> + | 5 | 1366x768 | NXP Generic |
>>>>>>> + | 6 | 1600x900 | ChiMei M215HGE |
>>>>>>> + +-------+------------+------------------+
>>>>>>> +
>>>>>>> +Example:
>>>>>>> + ptn3460-bridge@20 {
>>>>>>> + compatible = "nxp,ptn3460";
>>>>>>> + reg = <0x20>;
>>>>>>> + powerdown-gpio = <&gpy2 5 1 0 0>;
>>>>>>> + reset-gpio = <&gpx1 5 1 0 0>;
>>>>>>> + edid-emulation = <5>;
>>>>>>> + };
>>>>>>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>>>>>>> index 955555d..cd7bfb3 100644
>>>>>>> --- a/drivers/gpu/drm/Kconfig
>>>>>>> +++ b/drivers/gpu/drm/Kconfig
>>>>>>> @@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
>>>>>>> source "drivers/gpu/drm/qxl/Kconfig"
>>>>>>>
>>>>>>> source "drivers/gpu/drm/msm/Kconfig"
>>>>>>> +
>>>>>>> +source "drivers/gpu/drm/bridge/Kconfig"
>>>>>>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>>>>>>> index f089adf..9234253 100644
>>>>>>> --- a/drivers/gpu/drm/Makefile
>>>>>>> +++ b/drivers/gpu/drm/Makefile
>>>>>>> @@ -56,3 +56,4 @@ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
>>>>>>> obj-$(CONFIG_DRM_QXL) += qxl/
>>>>>>> obj-$(CONFIG_DRM_MSM) += msm/
>>>>>>> obj-y += i2c/
>>>>>>> +obj-y += bridge/
>>>>>>> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
>>>>>>> new file mode 100644
>>>>>>> index 0000000..f8db069
>>>>>>> --- /dev/null
>>>>>>> +++ b/drivers/gpu/drm/bridge/Kconfig
>>>>>>> @@ -0,0 +1,4 @@
>>>>>>> +config DRM_PTN3460
>>>>>>> + tristate "PTN3460 DP/LVDS bridge"
>>>>>>> + depends on DRM && I2C
>>>>>>> + ---help---
>>>>>>> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
>>>>>>> new file mode 100644
>>>>>>> index 0000000..b4733e1
>>>>>>> --- /dev/null
>>>>>>> +++ b/drivers/gpu/drm/bridge/Makefile
>>>>>>> @@ -0,0 +1,3 @@
>>>>>>> +ccflags-y := -Iinclude/drm
>>>>>>> +
>>>>>>> +obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
>>>>>>> diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
>>>>>>> new file mode 100644
>>>>>>> index 0000000..a9e5c1a
>>>>>>> --- /dev/null
>>>>>>> +++ b/drivers/gpu/drm/bridge/ptn3460.c
>>>>>>> @@ -0,0 +1,349 @@
>>>>>>> +/*
>>>>>>> + * NXP PTN3460 DP/LVDS bridge driver
>>>>>>> + *
>>>>>>> + * Copyright (C) 2013 Google, Inc.
>>>>>>> + *
>>>>>>> + * 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/of.h>
>>>>>>> +#include <linux/of_gpio.h>
>>>>>>> +#include <linux/i2c.h>
>>>>>>> +#include <linux/gpio.h>
>>>>>>> +#include <linux/delay.h>
>>>>>>> +
>>>>>>> +#include "drmP.h"
>>>>>>> +#include "drm_edid.h"
>>>>>>> +#include "drm_crtc.h"
>>>>>>> +#include "drm_crtc_helper.h"
>>>>>>> +
>>>>>>> +#include "bridge/ptn3460.h"
>>>>>>> +
>>>>>>> +#define PTN3460_EDID_ADDR 0x0
>>>>>>> +#define PTN3460_EDID_EMULATION_ADDR 0x84
>>>>>>> +#define PTN3460_EDID_ENABLE_EMULATION 0
>>>>>>> +#define PTN3460_EDID_EMULATION_SELECTION 1
>>>>>>> +#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
>>>>>>> +
>>>>>>> +struct ptn3460_bridge {
>>>>>>> + struct drm_connector connector;
>>>>>>> + struct i2c_client *client;
>>>>>>> + struct drm_encoder *encoder;
>>>>>>> + struct drm_bridge *bridge;
>>>>>>> + struct edid *edid;
>>>>>>> + int gpio_pd_n;
>>>>>>> + int gpio_rst_n;
>>>>>>> + u32 edid_emulation;
>>>>>>> + bool enabled;
>>>>>>> +};
>>>>>>> +
>>>>>>> +static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
>>>>>>> + u8 *buf, int len)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + ret = i2c_master_send(ptn_bridge->client, &addr, 1);
>>>>>>> + if (ret <= 0) {
>>>>>>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + ret = i2c_master_recv(ptn_bridge->client, buf, len);
>>>>>>> + if (ret <= 0) {
>>>>>>> + DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
>>>>>>> + char val)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> + char buf[2];
>>>>>>> +
>>>>>>> + buf[0] = addr;
>>>>>>> + buf[1] = val;
>>>>>>> +
>>>>>>> + ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
>>>>>>> + if (ret <= 0) {
>>>>>>> + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> + char val;
>>>>>>> +
>>>>>>> + /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
>>>>>>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
>>>>>>> + ptn_bridge->edid_emulation);
>>>>>>> + if (ret) {
>>>>>>> + DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + /* Enable EDID emulation and select the desired EDID */
>>>>>>> + val = 1 << PTN3460_EDID_ENABLE_EMULATION |
>>>>>>> + ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
>>>>>>> +
>>>>>>> + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
>>>>>>> + if (ret) {
>>>>>>> + DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void ptn3460_pre_enable(struct drm_bridge *bridge)
>>>>>>> +{
>>>>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + if (ptn_bridge->enabled)
>>>>>>> + return;
>>>>>>> +
>>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>>>> + gpio_set_value(ptn_bridge->gpio_pd_n, 1);
>>>>>>
>>>>>> Ditto.
>>>>>>
>>>>>>> +
>>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>>>>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 0);
>>>>>>> + udelay(10);
>>>>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>>>>>> + }
>>>>>>> +
>>>>>>> + /*
>>>>>>> + * There's a bug in the PTN chip where it falsely asserts hotplug before
>>>>>>> + * it is fully functional. We're forced to wait for the maximum start up
>>>>>>> + * time specified in the chip's datasheet to make sure we're really up.
>>>>>>> + */
>>>>>>> + msleep(90);
>>>>>>> +
>>>>>>> + ret = ptn3460_select_edid(ptn_bridge);
>>>>>>> + if (ret)
>>>>>>> + DRM_ERROR("Select edid failed ret=%d\n", ret);
>>>>>>> +
>>>>>>> + ptn_bridge->enabled = true;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void ptn3460_enable(struct drm_bridge *bridge)
>>>>>>> +{
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void ptn3460_disable(struct drm_bridge *bridge)
>>>>>>> +{
>>>>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>>>>> +
>>>>>>> + if (!ptn_bridge->enabled)
>>>>>>> + return;
>>>>>>> +
>>>>>>> + ptn_bridge->enabled = false;
>>>>>>> +
>>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>>>>> + gpio_set_value(ptn_bridge->gpio_rst_n, 1);
>>>>>>> +
>>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>>>> + gpio_set_value(ptn_bridge->gpio_pd_n, 0);
>>>>>>
>>>>>> Ditto.
>>>>>>
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void ptn3460_post_disable(struct drm_bridge *bridge)
>>>>>>> +{
>>>>>>> +}
>>>>>>> +
>>>>>>> +void ptn3460_bridge_destroy(struct drm_bridge *bridge)
>>>>>>> +{
>>>>>>> + struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
>>>>>>> +
>>>>>>> + drm_bridge_cleanup(bridge);
>>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>>>>
>>>>>> Ditto.
>>>>>>
>>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>>>>> + gpio_free(ptn_bridge->gpio_rst_n);
>>>>>>> + /* Nothing else to free, we've got devm allocated memory */
>>>>>>> +}
>>>>>>> +
>>>>>>> +struct drm_bridge_funcs ptn3460_bridge_funcs = {
>>>>>>> + .pre_enable = ptn3460_pre_enable,
>>>>>>> + .enable = ptn3460_enable,
>>>>>>> + .disable = ptn3460_disable,
>>>>>>> + .post_disable = ptn3460_post_disable,
>>>>>>> + .destroy = ptn3460_bridge_destroy,
>>>>>>> +};
>>>>>>> +
>>>>>>> +int ptn3460_get_modes(struct drm_connector *connector)
>>>>>>> +{
>>>>>>> + struct ptn3460_bridge *ptn_bridge;
>>>>>>> + u8 *edid;
>>>>>>> + int ret, num_modes;
>>>>>>> + bool power_off;
>>>>>>> +
>>>>>>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>>>>>>> +
>>>>>>> + if (ptn_bridge->edid)
>>>>>>> + return drm_add_edid_modes(connector, ptn_bridge->edid);
>>>>>>> +
>>>>>>> + power_off = !ptn_bridge->enabled;
>>>>>>> + ptn3460_pre_enable(ptn_bridge->bridge);
>>>>>>> +
>>>>>>> + edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
>>>>>>> + if (!edid) {
>>>>>>> + DRM_ERROR("Failed to allocate edid\n");
>>>>>>> + return 0;
>>>>>>> + }
>>>>>>> +
>>>>>>> + ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
>>>>>>> + EDID_LENGTH);
>>>>>>> + if (ret) {
>>>>>>> + kfree(edid);
>>>>>>> + num_modes = 0;
>>>>>>> + goto out;
>>>>>>> + }
>>>>>>> +
>>>>>>> + ptn_bridge->edid = (struct edid *)edid;
>>>>>>> + drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
>>>>>>> +
>>>>>>> + num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
>>>>>>> +
>>>>>>> +out:
>>>>>>> + if (power_off)
>>>>>>> + ptn3460_disable(ptn_bridge->bridge);
>>>>>>> +
>>>>>>> + return num_modes;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int ptn3460_mode_valid(struct drm_connector *connector,
>>>>>>> + struct drm_display_mode *mode)
>>>>>>> +{
>>>>>>> + return MODE_OK;
>>>>>>> +}
>>>>>>> +
>>>>>>> +struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
>>>>>>> +{
>>>>>>> + struct ptn3460_bridge *ptn_bridge;
>>>>>>> +
>>>>>>> + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
>>>>>>> +
>>>>>>> + return ptn_bridge->encoder;
>>>>>>> +}
>>>>>>> +
>>>>>>> +struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
>>>>>>> + .get_modes = ptn3460_get_modes,
>>>>>>> + .mode_valid = ptn3460_mode_valid,
>>>>>>> + .best_encoder = ptn3460_best_encoder,
>>>>>>> +};
>>>>>>> +
>>>>>>> +enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
>>>>>>> + bool force)
>>>>>>> +{
>>>>>>> + return connector_status_connected;
>>>>>>> +}
>>>>>>> +
>>>>>>> +void ptn3460_connector_destroy(struct drm_connector *connector)
>>>>>>> +{
>>>>>>> + drm_connector_cleanup(connector);
>>>>>>> +}
>>>>>>> +
>>>>>>> +struct drm_connector_funcs ptn3460_connector_funcs = {
>>>>>>> + .dpms = drm_helper_connector_dpms,
>>>>>>> + .fill_modes = drm_helper_probe_single_connector_modes,
>>>>>>> + .detect = ptn3460_detect,
>>>>>>> + .destroy = ptn3460_connector_destroy,
>>>>>>> +};
>>>>>>
>>>>>> Why do you try to add a new connector here? We already have the
>>>>>> connector for LCD, and also provides some callbacks for it. For this,
>>>>>> please see exynos_drm_display_ops of exynos_drm_fimd driver, and you
>>>>>> can add new callbacks to there such as init callback for bridge device
>>>>>> initialization if needed.
>>>>>>
>>>>>
>>>>> We add a new connector for 2 reasons:
>>>>>
>>>>> 1) We need to override the drm detect() callback to always return true
>>>>> since the DP driver will presumably return its hotplug status which
>>>>> will always be low when the ptn chip is turned off.
>>>>> 2) We want the ability to control the result of get_modes().
>>>>>
>>>>> I've got a patch set almost ready to tear the display ops out of fimd
>>>>> and put them in the DP driver.
>>>>
>>>> Really? if so, that is ideal something we want and we should go. But
>>>> isn't the DP driver placed in drivers/video/exynos? How did you take
>>>> care of that?
>>>
>>> git mv :)
>>>
>>
>> :)
>>
>>
>>>> Actually, for this, we planned to use CDF(Common Display
>>>> Framework) if the framework is merged to mainline somehow.
>>>>
>>>
>>> Right. I think CDF will end up being a series of improvements to drm,
>>> as opposed to its own framework (at least this was the conclusion I
>>> came to after speaking with Laurent at the plumbers conference). I
>>> don't think it makes sense to have the DP driver outside of drm. The
>>> HDMI driver is already inside drm, the DP driver should be too.
>>
>> See the below,
>>
>> Application
>> --------------------------------------------------------------
>> v4l2 drm kms
>> hdmi driver hdmi driver
>> -------------------------------------------------------------
>> hdmi hw
>>
>> HDMI is a controller can be controlled by user application, and for
>> this some frameworks such as v4l2 and drm kms interfaces are used. But
>> DP, MIPI-DSI, LVDS, and so on aren't controlled by user application.
>> These are just display bus between scanout devices(hdmi, fimd) and lcd
>> panel. So the above your example doesn't make sense.
>>
>
> I think we've probably gone far enough off-topic. Let's discuss this
> when you've had an opportunity to see the patchset. I hope the code
> will speak for itself.
>

Ok, let's have a discussion after looking into the codes.

> Aside from Olof's suggestion about changing the dts binding to
> lvds-bridge (which I'll upload a new patch for), do you have any
> suggestions for this patch?
>

lvds-bridge is better. It seems reasonable to me.

> Sean
>
>
>>> Regardless, this conversation is only tangentially related to this
>>> patch and can probably be deferred.
>>>
>>> Sean
>>>
>>>>> The display ops are better suited
>>>>> there, since it's the actual encoder/connector. I hope to get that
>>>>> posted this week.
>>>>>
>>>>
>>>> I will look forward to that posting. :)
>>>>
>>>>>
>>>>>>> +
>>>>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>>>>> + struct i2c_client *client, struct device_node *node)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> + struct drm_bridge *bridge;
>>>>>>> + struct ptn3460_bridge *ptn_bridge;
>>>>>>> +
>>>>>>> + bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
>>>>>>> + if (!bridge) {
>>>>>>> + DRM_ERROR("Failed to allocate drm bridge\n");
>>>>>>> + return -ENOMEM;
>>>>>>> + }
>>>>>>> +
>>>>>>> + ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
>>>>>>> + if (!ptn_bridge) {
>>>>>>> + DRM_ERROR("Failed to allocate ptn bridge\n");
>>>>>>> + return -ENOMEM;
>>>>>>> + }
>>>>>>> +
>>>>>>> + ptn_bridge->client = client;
>>>>>>> + ptn_bridge->encoder = encoder;
>>>>>>> + ptn_bridge->bridge = bridge;
>>>>>>> + ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
>>>>>>
>>>>>> Also, if a regulator is used instead?
>>>>>>
>>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
>>>>>>> + ret = gpio_request_one(ptn_bridge->gpio_pd_n,
>>>>>>> + GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
>>>>>>> + if (ret) {
>>>>>>> + DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> + }
>>>>>>> +
>>>>>>> + ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
>>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
>>>>>>> + /*
>>>>>>> + * Request the reset pin low to avoid the bridge being
>>>>>>> + * initialized prematurely
>>>>>>> + */
>>>>>>> + ret = gpio_request_one(ptn_bridge->gpio_rst_n,
>>>>>>> + GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
>>>>>>> + if (ret) {
>>>>>>> + DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
>>>>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> + }
>>>>>>> +
>>>>>>> + ret = of_property_read_u32(node, "edid-emulation",
>>>>>>> + &ptn_bridge->edid_emulation);
>>>>>>> + if (ret) {
>>>>>>> + DRM_ERROR("Can't read edid emulation value\n");
>>>>>>> + goto err;
>>>>>>> + }
>>>>>>> +
>>>>>>> + ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
>>>>>>> + if (ret) {
>>>>>>> + DRM_ERROR("Failed to initialize bridge with drm\n");
>>>>>>> + goto err;
>>>>>>> + }
>>>>>>> +
>>>>>>> + bridge->driver_private = ptn_bridge;
>>>>>>> + encoder->bridge = bridge;
>>>>>>> +
>>>>>>> + ret = drm_connector_init(dev, &ptn_bridge->connector,
>>>>>>> + &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
>>>>>>
>>>>>> So it seems that here's not right place to call drm_connector_init function.
>>>>>>
>>>>>> Display pipeline path could be one of,
>>>>>> Display Controller Display bus
>>>>>> ---------------------------------------------------------------------------
>>>>>> FIMD---------------------LVDS--------------------LCD,
>>>>>> or
>>>>>> FIMD----------------------eDP---------------------LCD,
>>>>>> or
>>>>>> FIMD------------------MIPI-DSI------------------LCD,
>>>>>> or
>>>>>> FIMD-------------------------------------------------LCD
>>>>>>
>>>>>> And also in case using image enhancement chip,
>>>>>> mDNIe-------------FIMD-LITE between Display Controller and Display
>>>>>> bus.
>>>>>>
>>>>>> So, wouldn't the right place below FIMD driver? :)
>>>>>>
>>>>>
>>>>> Well, this driver should be considered outside of exynos context since
>>>>> it could be used by any drm driver.
>>>>>
>>>>> In the exynos context, the right place to implement it would be in the
>>>>> dp driver, actually. However, the exynos driver has a level of
>>>>> abstraction on top of the crtcs/encoders such that we need to
>>>>> initialize it in the exynos_drm_core. The patchset I mentioned above
>>>>> should help move things in a direction where fimd/mixer implement
>>>>> drm_crtc directly and hdmi/dp implement drm_encoder/drm_connector
>>>>> directly. In that world, DP would initialize the ptn driver.
>>>>>
>>>>>>
>>>>>>> + if (ret) {
>>>>>>> + DRM_ERROR("Failed to initialize connector with drm\n");
>>>>>>> + goto err;
>>>>>>> + }
>>>>>>> + drm_connector_helper_add(&ptn_bridge->connector,
>>>>>>> + &ptn3460_connector_helper_funcs);
>>>>>>> + drm_sysfs_connector_add(&ptn_bridge->connector);
>>>>>>> + drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
>>>>>>> +
>>>>>>> + return 0;
>>>>>>> +
>>>>>>> +err:
>>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_pd_n))
>>>>>>> + gpio_free(ptn_bridge->gpio_pd_n);
>>>>>>> + if (gpio_is_valid(ptn_bridge->gpio_rst_n))
>>>>>>> + gpio_free(ptn_bridge->gpio_rst_n);
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
>>>>>>> new file mode 100644
>>>>>>> index 0000000..157ffa1
>>>>>>> --- /dev/null
>>>>>>> +++ b/include/drm/bridge/ptn3460.h
>>>>>>> @@ -0,0 +1,36 @@
>>>>>>> +/*
>>>>>>> + * Copyright (C) 2013 Google, Inc.
>>>>>>> + *
>>>>>>> + * 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 _DRM_BRIDGE_PTN3460_H_
>>>>>>> +#define _DRM_BRIDGE_PTN3460_H_
>>>>>>> +
>>>>>>> +struct drm_device;
>>>>>>> +struct drm_encoder;
>>>>>>> +struct i2c_client;
>>>>>>> +struct device_node;
>>>>>>> +
>>>>>>> +#ifdef CONFIG_DRM_PTN3460
>>>>>>> +
>>>>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>>>>> + struct i2c_client *client, struct device_node *node);
>>>>>>> +#else
>>>>>>> +
>>>>>>> +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
>>>>>>> + struct i2c_client *client, struct device_node *node)
>>>>>>> +{
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +#endif
>>>>>>> +
>>>>>>> +#endif
>>>>>>> --
>>>>>>> 1.8.4
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> linux-arm-kernel mailing list
>>>>>>> [email protected]
>>>>>>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> [email protected]
>>>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>>> _______________________________________________
>>> dri-devel mailing list
>>> [email protected]
>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> _______________________________________________
> dri-devel mailing list
> [email protected]
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

2013/10/4 Olof Johansson <[email protected]>:
> On Thu, Oct 3, 2013 at 10:39 AM, Inki Dae <[email protected]> wrote:
>> 2013/10/3 Sean Paul <[email protected]>:
>>> On Thu, Oct 3, 2013 at 9:55 AM, Inki Dae <[email protected]> wrote:
>>>> Can a regulator be used instead of gpio in other board case?
>>>>
>>>
>>> No, not to my knowledge.
>>>
>>
>> Hm.. plz check it out again. the gpio pin is specific to board, and
>> the the gpio be used as power source trigger could be replaced with a
>> regulator according to board design. So you should consider all
>> possibilities even though there are no other cases yet: other board
>> could use a regulator instead.
>
> Take a look at the data sheet, it is publicly available.
>
> PD_N is not a power supply input, so modelling it as a regulator makes no sense:
>
> "If PD_N is LOW, then the device is in Deep power-down completely,
> even if supply rail is ON; for the device to be able to operate, the
> PD_N pin must be HIGH."
>

I still think the pin could be replaced with a regulator. But
lvds-bridge node has "powerdown-gpio" property - it say this board
will use gpio pin - specific to board. So it seems no problem.

>
>
> -Olof
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-10-04 02:21:23

by Jingoo Han

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] ARM: dts: Add fimd display-timings for exynos5250-snow

On Friday, October 04, 2013 7:28 AM, Sean Paul wrote:
>
> This patch adds the internal panel timings to the exynos5250-snow board
> dts file.
>
> Signed-off-by: Sean Paul <[email protected]>
> ---
>
> v2: No difference
>
> arch/arm/boot/dts/exynos5250-snow.dts | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
> index fd711e2..2a0f0de 100644
> --- a/arch/arm/boot/dts/exynos5250-snow.dts
> +++ b/arch/arm/boot/dts/exynos5250-snow.dts
> @@ -186,6 +186,23 @@
> samsung,vbus-gpio = <&gpx1 1 0>;
> };
>
> + fimd: fimd@14400000 {
> + display-timings {

How about adding 'status = "okay"' as below?

fimd: fimd@14400000 {
status = "okay";
display-timings {

status of fimd node was disabled by exynos5.dtsi

./arch/arm/boot/dts/exynos5.dtsi
fimd@14400000 {
status = "disabled";

Best regards,
Jingoo Han

2013-10-04 02:25:41

by Jingoo Han

[permalink] [raw]
Subject: Re: [PATCH v2 2/5] ARM: dts: Add dp-controller node to exynos5250-snow

On Friday, October 04, 2013 7:28 AM, Sean Paul wrote:
>
> This patch adds the dp-controller node to the exynos5250-snow board dts
> file.
>
> Signed-off-by: Sean Paul <[email protected]>
> ---
>
> v2: Added dp-controller address to node (rebased on linux-next)
>
> arch/arm/boot/dts/exynos5250-snow.dts | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
> index 2a0f0de..f1cf68e 100644
> --- a/arch/arm/boot/dts/exynos5250-snow.dts
> +++ b/arch/arm/boot/dts/exynos5250-snow.dts
> @@ -186,6 +186,18 @@
> samsung,vbus-gpio = <&gpx1 1 0>;
> };
>
> + dp-controller@145B0000 {
> + samsung,color-space = <0>;
> + samsung,dynamic-range = <0>;
> + samsung,ycbcr-coeff = <0>;
> + samsung,color-depth = <1>;
> + samsung,link-rate = <0x0a>;
> + samsung,lane-count = <2>;
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&dp_hpd>;

How about adding 'status = "okay" as below?

pinctrl-names = "default";
pinctrl-0 = <&dp_hpd>;
status = "okay";

status of dp node was disabled by exynos5.dtsi.

./arch/arm/boot/dts/exynos5.dtsi
dp-controller@145B0000 {
status = "disabled";

Best regards,
Jingoo Han

Subject: Re: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present

2013/10/4 Sean Paul <[email protected]>:
> This patch adds code to look for the ptn3460 in the device tree file on
> exynos initialization. If ptn node is found, the driver will initialize
> the ptn3460 driver and skip creating a DP connector (since the bridge
> driver will register its own connector).
>
> Signed-off-by: Sean Paul <[email protected]>
> ---
>
> v2:
> - Changed include from of_i2c.h to i2c.h
> - Changed of_find_by_name to of_find_compatible
>
> drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
> 1 file changed, 43 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
> index 1bef6dc..08ca4f9 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
> @@ -12,7 +12,9 @@
> * option) any later version.
> */
>
> +#include <linux/i2c.h>
> #include <drm/drmP.h>
> +#include <drm/bridge/ptn3460.h>
> #include "exynos_drm_drv.h"
> #include "exynos_drm_encoder.h"
> #include "exynos_drm_connector.h"
> @@ -20,6 +22,40 @@
>
> static LIST_HEAD(exynos_drm_subdrv_list);
>
> +struct bridge_init {
> + struct i2c_client *client;
> + struct device_node *node;
> +};
> +
> +static bool find_bridge(const char *compat, struct bridge_init *bridge)
> +{
> + bridge->client = NULL;
> + bridge->node = of_find_compatible_node(NULL, NULL, compat);

Then, shouldn't the lvds-bridge driver be implemented as i2c driver so
that such tricky isn't needed? Is there any reason you use such tricky
codes?

I think you need to implement the lvds-bridge driver as i2c driver,
and then consider how to take care of this. I mean let's find a better
way how we could take care of the i2c based driver in exynos drm
framework or driver. In all cases, such tricky codes are not good.

> + if (!bridge->node)
> + return false;
> +
> + bridge->client = of_find_i2c_device_by_node(bridge->node);
> + if (!bridge->client)
> + return false;
> +
> + return true;
> +}
> +
> +/* returns the number of bridges attached */
> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
> + struct drm_encoder *encoder)
> +{
> + struct bridge_init bridge;
> + int ret;
> +
> + if (find_bridge("nxp,ptn3460", &bridge)) {
> + ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
> + if (!ret)
> + return 1;
> + }
> + return 0;
> +}
> +
> static int exynos_drm_create_enc_conn(struct drm_device *dev,
> struct exynos_drm_subdrv *subdrv)
> {
> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
> DRM_ERROR("failed to create encoder\n");
> return -EFAULT;
> }
> + subdrv->encoder = encoder;
> +
> + if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
> + if (ret)
> + return 0;
> + }
>
> /*
> * create and initialize a connector for this sub driver and
> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
> goto err_destroy_encoder;
> }
>
> - subdrv->encoder = encoder;
> subdrv->connector = connector;
>
> return 0;
> --
> 1.8.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-10-04 02:46:52

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present

On Thu, Oct 3, 2013 at 10:29 PM, Inki Dae <[email protected]> wrote:
> 2013/10/4 Sean Paul <[email protected]>:
>> This patch adds code to look for the ptn3460 in the device tree file on
>> exynos initialization. If ptn node is found, the driver will initialize
>> the ptn3460 driver and skip creating a DP connector (since the bridge
>> driver will register its own connector).
>>
>> Signed-off-by: Sean Paul <[email protected]>
>> ---
>>
>> v2:
>> - Changed include from of_i2c.h to i2c.h
>> - Changed of_find_by_name to of_find_compatible
>>
>> drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
>> 1 file changed, 43 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
>> index 1bef6dc..08ca4f9 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
>> @@ -12,7 +12,9 @@
>> * option) any later version.
>> */
>>
>> +#include <linux/i2c.h>
>> #include <drm/drmP.h>
>> +#include <drm/bridge/ptn3460.h>
>> #include "exynos_drm_drv.h"
>> #include "exynos_drm_encoder.h"
>> #include "exynos_drm_connector.h"
>> @@ -20,6 +22,40 @@
>>
>> static LIST_HEAD(exynos_drm_subdrv_list);
>>
>> +struct bridge_init {
>> + struct i2c_client *client;
>> + struct device_node *node;
>> +};
>> +
>> +static bool find_bridge(const char *compat, struct bridge_init *bridge)
>> +{
>> + bridge->client = NULL;
>> + bridge->node = of_find_compatible_node(NULL, NULL, compat);
>
> Then, shouldn't the lvds-bridge driver be implemented as i2c driver so
> that such tricky isn't needed? Is there any reason you use such tricky
> codes?
>

This is what I was explaining earlier today. If the bridge driver is
just an i2c driver, it won't get a pointer to drm_device, and can't
register itself as a drm_bridge or drm_connector. To solve this, you
need the ptn3460_init callback.

If it's an i2c driver with the ptn3460_init callback, where it parses
the dt in probe, then you have a race between the ptn probe and the
ptn init/drm hooks. ie: it's possible drm will initialize and call
disable/post_disable/pre_enable/enable before things have been probed.
To solve this, you need to use some global state and/or locking.

So, to summarize. We can have this of_find_compatible with a init
callback, or we can have an i2c driver + global state/locking + init
callback. I chose the former, since it seemed easier.

If you have a better way to achieve this, I'm definitely interested,
but I saw this as the lesser of two evils.

Sean

> I think you need to implement the lvds-bridge driver as i2c driver,
> and then consider how to take care of this. I mean let's find a better
> way how we could take care of the i2c based driver in exynos drm
> framework or driver. In all cases, such tricky codes are not good.
>
>> + if (!bridge->node)
>> + return false;
>> +
>> + bridge->client = of_find_i2c_device_by_node(bridge->node);
>> + if (!bridge->client)
>> + return false;
>> +
>> + return true;
>> +}
>> +
>> +/* returns the number of bridges attached */
>> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>> + struct drm_encoder *encoder)
>> +{
>> + struct bridge_init bridge;
>> + int ret;
>> +
>> + if (find_bridge("nxp,ptn3460", &bridge)) {
>> + ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
>> + if (!ret)
>> + return 1;
>> + }
>> + return 0;
>> +}
>> +
>> static int exynos_drm_create_enc_conn(struct drm_device *dev,
>> struct exynos_drm_subdrv *subdrv)
>> {
>> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>> DRM_ERROR("failed to create encoder\n");
>> return -EFAULT;
>> }
>> + subdrv->encoder = encoder;
>> +
>> + if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
>> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
>> + if (ret)
>> + return 0;
>> + }
>>
>> /*
>> * create and initialize a connector for this sub driver and
>> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>> goto err_destroy_encoder;
>> }
>>
>> - subdrv->encoder = encoder;
>> subdrv->connector = connector;
>>
>> return 0;
>> --
>> 1.8.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html

Subject: Re: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present

2013/10/4 Sean Paul <[email protected]>:
> On Thu, Oct 3, 2013 at 10:29 PM, Inki Dae <[email protected]> wrote:
>> 2013/10/4 Sean Paul <[email protected]>:
>>> This patch adds code to look for the ptn3460 in the device tree file on
>>> exynos initialization. If ptn node is found, the driver will initialize
>>> the ptn3460 driver and skip creating a DP connector (since the bridge
>>> driver will register its own connector).
>>>
>>> Signed-off-by: Sean Paul <[email protected]>
>>> ---
>>>
>>> v2:
>>> - Changed include from of_i2c.h to i2c.h
>>> - Changed of_find_by_name to of_find_compatible
>>>
>>> drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
>>> 1 file changed, 43 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
>>> index 1bef6dc..08ca4f9 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
>>> @@ -12,7 +12,9 @@
>>> * option) any later version.
>>> */
>>>
>>> +#include <linux/i2c.h>
>>> #include <drm/drmP.h>
>>> +#include <drm/bridge/ptn3460.h>
>>> #include "exynos_drm_drv.h"
>>> #include "exynos_drm_encoder.h"
>>> #include "exynos_drm_connector.h"
>>> @@ -20,6 +22,40 @@
>>>
>>> static LIST_HEAD(exynos_drm_subdrv_list);
>>>
>>> +struct bridge_init {
>>> + struct i2c_client *client;
>>> + struct device_node *node;
>>> +};
>>> +
>>> +static bool find_bridge(const char *compat, struct bridge_init *bridge)
>>> +{
>>> + bridge->client = NULL;
>>> + bridge->node = of_find_compatible_node(NULL, NULL, compat);
>>
>> Then, shouldn't the lvds-bridge driver be implemented as i2c driver so
>> that such tricky isn't needed? Is there any reason you use such tricky
>> codes?
>>
>
> This is what I was explaining earlier today. If the bridge driver is
> just an i2c driver, it won't get a pointer to drm_device, and can't
> register itself as a drm_bridge or drm_connector. To solve this, you
> need the ptn3460_init callback.
>

No, I think you can use sub driver. how about registering to exynos
drm core that driver using exynos_drm_subdrv_register function after
probed? Is there something I am missing?


> If it's an i2c driver with the ptn3460_init callback, where it parses
> the dt in probe, then you have a race between the ptn probe and the
> ptn init/drm hooks. ie: it's possible drm will initialize and call
> disable/post_disable/pre_enable/enable before things have been probed.

And also, exynos drm core will call a probe callback of the sub driver
after _drm is initialized_. Is there something I am missing?

> To solve this, you need to use some global state and/or locking.
>
> So, to summarize. We can have this of_find_compatible with a init
> callback, or we can have an i2c driver + global state/locking + init
> callback. I chose the former, since it seemed easier.
>
> If you have a better way to achieve this, I'm definitely interested,
> but I saw this as the lesser of two evils.
>
> Sean
>
>> I think you need to implement the lvds-bridge driver as i2c driver,
>> and then consider how to take care of this. I mean let's find a better
>> way how we could take care of the i2c based driver in exynos drm
>> framework or driver. In all cases, such tricky codes are not good.
>>
>>> + if (!bridge->node)
>>> + return false;
>>> +
>>> + bridge->client = of_find_i2c_device_by_node(bridge->node);
>>> + if (!bridge->client)
>>> + return false;
>>> +
>>> + return true;
>>> +}
>>> +
>>> +/* returns the number of bridges attached */
>>> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>>> + struct drm_encoder *encoder)
>>> +{
>>> + struct bridge_init bridge;
>>> + int ret;
>>> +
>>> + if (find_bridge("nxp,ptn3460", &bridge)) {
>>> + ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
>>> + if (!ret)
>>> + return 1;
>>> + }
>>> + return 0;
>>> +}
>>> +
>>> static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>> struct exynos_drm_subdrv *subdrv)
>>> {
>>> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>> DRM_ERROR("failed to create encoder\n");
>>> return -EFAULT;
>>> }
>>> + subdrv->encoder = encoder;
>>> +
>>> + if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
>>> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
>>> + if (ret)
>>> + return 0;
>>> + }
>>>
>>> /*
>>> * create and initialize a connector for this sub driver and
>>> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>> goto err_destroy_encoder;
>>> }
>>>
>>> - subdrv->encoder = encoder;
>>> subdrv->connector = connector;
>>>
>>> return 0;
>>> --
>>> 1.8.4
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>>> the body of a message to [email protected]
>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-10-04 14:17:51

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present

On Fri, Oct 4, 2013 at 12:18 AM, Inki Dae <[email protected]> wrote:
> 2013/10/4 Sean Paul <[email protected]>:
>> On Thu, Oct 3, 2013 at 10:29 PM, Inki Dae <[email protected]> wrote:
>>> 2013/10/4 Sean Paul <[email protected]>:
>>>> This patch adds code to look for the ptn3460 in the device tree file on
>>>> exynos initialization. If ptn node is found, the driver will initialize
>>>> the ptn3460 driver and skip creating a DP connector (since the bridge
>>>> driver will register its own connector).
>>>>
>>>> Signed-off-by: Sean Paul <[email protected]>
>>>> ---
>>>>
>>>> v2:
>>>> - Changed include from of_i2c.h to i2c.h
>>>> - Changed of_find_by_name to of_find_compatible
>>>>
>>>> drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
>>>> 1 file changed, 43 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
>>>> index 1bef6dc..08ca4f9 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
>>>> @@ -12,7 +12,9 @@
>>>> * option) any later version.
>>>> */
>>>>
>>>> +#include <linux/i2c.h>
>>>> #include <drm/drmP.h>
>>>> +#include <drm/bridge/ptn3460.h>
>>>> #include "exynos_drm_drv.h"
>>>> #include "exynos_drm_encoder.h"
>>>> #include "exynos_drm_connector.h"
>>>> @@ -20,6 +22,40 @@
>>>>
>>>> static LIST_HEAD(exynos_drm_subdrv_list);
>>>>
>>>> +struct bridge_init {
>>>> + struct i2c_client *client;
>>>> + struct device_node *node;
>>>> +};
>>>> +
>>>> +static bool find_bridge(const char *compat, struct bridge_init *bridge)
>>>> +{
>>>> + bridge->client = NULL;
>>>> + bridge->node = of_find_compatible_node(NULL, NULL, compat);
>>>
>>> Then, shouldn't the lvds-bridge driver be implemented as i2c driver so
>>> that such tricky isn't needed? Is there any reason you use such tricky
>>> codes?
>>>
>>
>> This is what I was explaining earlier today. If the bridge driver is
>> just an i2c driver, it won't get a pointer to drm_device, and can't
>> register itself as a drm_bridge or drm_connector. To solve this, you
>> need the ptn3460_init callback.
>>
>
> No, I think you can use sub driver. how about registering to exynos
> drm core that driver using exynos_drm_subdrv_register function after
> probed? Is there something I am missing?
>

The ptn driver isn't exynos-specific, so I don't think making it an
exynos_drm_subdrv is appropriate.

Sean

>
>> If it's an i2c driver with the ptn3460_init callback, where it parses
>> the dt in probe, then you have a race between the ptn probe and the
>> ptn init/drm hooks. ie: it's possible drm will initialize and call
>> disable/post_disable/pre_enable/enable before things have been probed.
>
> And also, exynos drm core will call a probe callback of the sub driver
> after _drm is initialized_. Is there something I am missing?
>
>> To solve this, you need to use some global state and/or locking.
>>
>> So, to summarize. We can have this of_find_compatible with a init
>> callback, or we can have an i2c driver + global state/locking + init
>> callback. I chose the former, since it seemed easier.
>>
>> If you have a better way to achieve this, I'm definitely interested,
>> but I saw this as the lesser of two evils.
>>
>> Sean
>>
>>> I think you need to implement the lvds-bridge driver as i2c driver,
>>> and then consider how to take care of this. I mean let's find a better
>>> way how we could take care of the i2c based driver in exynos drm
>>> framework or driver. In all cases, such tricky codes are not good.
>>>
>>>> + if (!bridge->node)
>>>> + return false;
>>>> +
>>>> + bridge->client = of_find_i2c_device_by_node(bridge->node);
>>>> + if (!bridge->client)
>>>> + return false;
>>>> +
>>>> + return true;
>>>> +}
>>>> +
>>>> +/* returns the number of bridges attached */
>>>> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>>>> + struct drm_encoder *encoder)
>>>> +{
>>>> + struct bridge_init bridge;
>>>> + int ret;
>>>> +
>>>> + if (find_bridge("nxp,ptn3460", &bridge)) {
>>>> + ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
>>>> + if (!ret)
>>>> + return 1;
>>>> + }
>>>> + return 0;
>>>> +}
>>>> +
>>>> static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>>> struct exynos_drm_subdrv *subdrv)
>>>> {
>>>> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>>> DRM_ERROR("failed to create encoder\n");
>>>> return -EFAULT;
>>>> }
>>>> + subdrv->encoder = encoder;
>>>> +
>>>> + if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
>>>> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
>>>> + if (ret)
>>>> + return 0;
>>>> + }
>>>>
>>>> /*
>>>> * create and initialize a connector for this sub driver and
>>>> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
>>>> goto err_destroy_encoder;
>>>> }
>>>>
>>>> - subdrv->encoder = encoder;
>>>> subdrv->connector = connector;
>>>>
>>>> return 0;
>>>> --
>>>> 1.8.4
>>>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>>>> the body of a message to [email protected]
>>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html

Subject: RE: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present



> -----Original Message-----
> From: Sean Paul [mailto:[email protected]]
> Sent: Friday, October 04, 2013 11:17 PM
> To: Inki Dae
> Cc: Kukjin Kim; DRI mailing list; [email protected];
> [email protected]; [email protected]; linux-
> [email protected]; [email protected]; Dave Airlie
> Subject: Re: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present
>
> On Fri, Oct 4, 2013 at 12:18 AM, Inki Dae <[email protected]> wrote:
> > 2013/10/4 Sean Paul <[email protected]>:
> >> On Thu, Oct 3, 2013 at 10:29 PM, Inki Dae <[email protected]> wrote:
> >>> 2013/10/4 Sean Paul <[email protected]>:
> >>>> This patch adds code to look for the ptn3460 in the device tree file
> on
> >>>> exynos initialization. If ptn node is found, the driver will
> initialize
> >>>> the ptn3460 driver and skip creating a DP connector (since the bridge
> >>>> driver will register its own connector).
> >>>>
> >>>> Signed-off-by: Sean Paul <[email protected]>
> >>>> ---
> >>>>
> >>>> v2:
> >>>> - Changed include from of_i2c.h to i2c.h
> >>>> - Changed of_find_by_name to of_find_compatible
> >>>>
> >>>> drivers/gpu/drm/exynos/exynos_drm_core.c | 44
> +++++++++++++++++++++++++++++++-
> >>>> 1 file changed, 43 insertions(+), 1 deletion(-)
> >>>>
> >>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c
> b/drivers/gpu/drm/exynos/exynos_drm_core.c
> >>>> index 1bef6dc..08ca4f9 100644
> >>>> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
> >>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
> >>>> @@ -12,7 +12,9 @@
> >>>> * option) any later version.
> >>>> */
> >>>>
> >>>> +#include <linux/i2c.h>
> >>>> #include <drm/drmP.h>
> >>>> +#include <drm/bridge/ptn3460.h>
> >>>> #include "exynos_drm_drv.h"
> >>>> #include "exynos_drm_encoder.h"
> >>>> #include "exynos_drm_connector.h"
> >>>> @@ -20,6 +22,40 @@
> >>>>
> >>>> static LIST_HEAD(exynos_drm_subdrv_list);
> >>>>
> >>>> +struct bridge_init {
> >>>> + struct i2c_client *client;
> >>>> + struct device_node *node;
> >>>> +};
> >>>> +
> >>>> +static bool find_bridge(const char *compat, struct bridge_init
> *bridge)
> >>>> +{
> >>>> + bridge->client = NULL;
> >>>> + bridge->node = of_find_compatible_node(NULL, NULL, compat);
> >>>
> >>> Then, shouldn't the lvds-bridge driver be implemented as i2c driver so
> >>> that such tricky isn't needed? Is there any reason you use such tricky
> >>> codes?
> >>>
> >>
> >> This is what I was explaining earlier today. If the bridge driver is
> >> just an i2c driver, it won't get a pointer to drm_device, and can't
> >> register itself as a drm_bridge or drm_connector. To solve this, you
> >> need the ptn3460_init callback.
> >>
> >
> > No, I think you can use sub driver. how about registering to exynos
> > drm core that driver using exynos_drm_subdrv_register function after
> > probed? Is there something I am missing?
> >
>
> The ptn driver isn't exynos-specific, so I don't think making it an
> exynos_drm_subdrv is appropriate.
>

I _really know_ that the ptn driver isn't exynos-specific. I mean that you
can use exynos_drm_subdrv somehow. ie. you can add a new bridge module
specific to exynos, and this module can register its own sub driver at end
of probe. Why do you try to use such tricky codes?

Thanks,
Inki Dae

> Sean
>
> >
> >> If it's an i2c driver with the ptn3460_init callback, where it parses
> >> the dt in probe, then you have a race between the ptn probe and the
> >> ptn init/drm hooks. ie: it's possible drm will initialize and call
> >> disable/post_disable/pre_enable/enable before things have been probed.
> >
> > And also, exynos drm core will call a probe callback of the sub driver
> > after _drm is initialized_. Is there something I am missing?
> >
> >> To solve this, you need to use some global state and/or locking.
> >>
> >> So, to summarize. We can have this of_find_compatible with a init
> >> callback, or we can have an i2c driver + global state/locking + init
> >> callback. I chose the former, since it seemed easier.
> >>
> >> If you have a better way to achieve this, I'm definitely interested,
> >> but I saw this as the lesser of two evils.
> >>
> >> Sean
> >>
> >>> I think you need to implement the lvds-bridge driver as i2c driver,
> >>> and then consider how to take care of this. I mean let's find a better
> >>> way how we could take care of the i2c based driver in exynos drm
> >>> framework or driver. In all cases, such tricky codes are not good.
> >>>
> >>>> + if (!bridge->node)
> >>>> + return false;
> >>>> +
> >>>> + bridge->client = of_find_i2c_device_by_node(bridge->node);
> >>>> + if (!bridge->client)
> >>>> + return false;
> >>>> +
> >>>> + return true;
> >>>> +}
> >>>> +
> >>>> +/* returns the number of bridges attached */
> >>>> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
> >>>> + struct drm_encoder *encoder)
> >>>> +{
> >>>> + struct bridge_init bridge;
> >>>> + int ret;
> >>>> +
> >>>> + if (find_bridge("nxp,ptn3460", &bridge)) {
> >>>> + ret = ptn3460_init(dev, encoder, bridge.client,
> bridge.node);
> >>>> + if (!ret)
> >>>> + return 1;
> >>>> + }
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> static int exynos_drm_create_enc_conn(struct drm_device *dev,
> >>>> struct exynos_drm_subdrv
*subdrv)
> >>>> {
> >>>> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct
> drm_device *dev,
> >>>> DRM_ERROR("failed to create encoder\n");
> >>>> return -EFAULT;
> >>>> }
> >>>> + subdrv->encoder = encoder;
> >>>> +
> >>>> + if (subdrv->manager->display_ops->type ==
> EXYNOS_DISPLAY_TYPE_LCD) {
> >>>> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
> >>>> + if (ret)
> >>>> + return 0;
> >>>> + }
> >>>>
> >>>> /*
> >>>> * create and initialize a connector for this sub driver and
> >>>> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct
> drm_device *dev,
> >>>> goto err_destroy_encoder;
> >>>> }
> >>>>
> >>>> - subdrv->encoder = encoder;
> >>>> subdrv->connector = connector;
> >>>>
> >>>> return 0;
> >>>> --
> >>>> 1.8.4
> >>>>
> >>>> --
> >>>> To unsubscribe from this list: send the line "unsubscribe linux-
> samsung-soc" in
> >>>> the body of a message to [email protected]
> >>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> >> --
> >> To unsubscribe from this list: send the line "unsubscribe linux-
> samsung-soc" in
> >> the body of a message to [email protected]
> >> More majordomo info at http://vger.kernel.org/majordomo-info.html

2013-10-04 15:05:16

by Sean Paul

[permalink] [raw]
Subject: Re: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present

On Fri, Oct 4, 2013 at 11:01 AM, Inki Dae <[email protected]> wrote:
>
>
>> -----Original Message-----
>> From: Sean Paul [mailto:[email protected]]
>> Sent: Friday, October 04, 2013 11:17 PM
>> To: Inki Dae
>> Cc: Kukjin Kim; DRI mailing list; [email protected];
>> [email protected]; [email protected]; linux-
>> [email protected]; [email protected]; Dave Airlie
>> Subject: Re: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present
>>
>> On Fri, Oct 4, 2013 at 12:18 AM, Inki Dae <[email protected]> wrote:
>> > 2013/10/4 Sean Paul <[email protected]>:
>> >> On Thu, Oct 3, 2013 at 10:29 PM, Inki Dae <[email protected]> wrote:
>> >>> 2013/10/4 Sean Paul <[email protected]>:
>> >>>> This patch adds code to look for the ptn3460 in the device tree file
>> on
>> >>>> exynos initialization. If ptn node is found, the driver will
>> initialize
>> >>>> the ptn3460 driver and skip creating a DP connector (since the bridge
>> >>>> driver will register its own connector).
>> >>>>
>> >>>> Signed-off-by: Sean Paul <[email protected]>
>> >>>> ---
>> >>>>
>> >>>> v2:
>> >>>> - Changed include from of_i2c.h to i2c.h
>> >>>> - Changed of_find_by_name to of_find_compatible
>> >>>>
>> >>>> drivers/gpu/drm/exynos/exynos_drm_core.c | 44
>> +++++++++++++++++++++++++++++++-
>> >>>> 1 file changed, 43 insertions(+), 1 deletion(-)
>> >>>>
>> >>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c
>> b/drivers/gpu/drm/exynos/exynos_drm_core.c
>> >>>> index 1bef6dc..08ca4f9 100644
>> >>>> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
>> >>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
>> >>>> @@ -12,7 +12,9 @@
>> >>>> * option) any later version.
>> >>>> */
>> >>>>
>> >>>> +#include <linux/i2c.h>
>> >>>> #include <drm/drmP.h>
>> >>>> +#include <drm/bridge/ptn3460.h>
>> >>>> #include "exynos_drm_drv.h"
>> >>>> #include "exynos_drm_encoder.h"
>> >>>> #include "exynos_drm_connector.h"
>> >>>> @@ -20,6 +22,40 @@
>> >>>>
>> >>>> static LIST_HEAD(exynos_drm_subdrv_list);
>> >>>>
>> >>>> +struct bridge_init {
>> >>>> + struct i2c_client *client;
>> >>>> + struct device_node *node;
>> >>>> +};
>> >>>> +
>> >>>> +static bool find_bridge(const char *compat, struct bridge_init
>> *bridge)
>> >>>> +{
>> >>>> + bridge->client = NULL;
>> >>>> + bridge->node = of_find_compatible_node(NULL, NULL, compat);
>> >>>
>> >>> Then, shouldn't the lvds-bridge driver be implemented as i2c driver so
>> >>> that such tricky isn't needed? Is there any reason you use such tricky
>> >>> codes?
>> >>>
>> >>
>> >> This is what I was explaining earlier today. If the bridge driver is
>> >> just an i2c driver, it won't get a pointer to drm_device, and can't
>> >> register itself as a drm_bridge or drm_connector. To solve this, you
>> >> need the ptn3460_init callback.
>> >>
>> >
>> > No, I think you can use sub driver. how about registering to exynos
>> > drm core that driver using exynos_drm_subdrv_register function after
>> > probed? Is there something I am missing?
>> >
>>
>> The ptn driver isn't exynos-specific, so I don't think making it an
>> exynos_drm_subdrv is appropriate.
>>
>
> I _really know_ that the ptn driver isn't exynos-specific. I mean that you
> can use exynos_drm_subdrv somehow. ie. you can add a new bridge module
> specific to exynos, and this module can register its own sub driver at end
> of probe. Why do you try to use such tricky codes?
>

So I create a new exynos_lvds_driver which is an i2c driver. When that
probes, all it does is register that driver as an exynos_drm_subdrv.
Then in the subdrv probe I call into ptn3460_init?

That seems a lot more tricky than just calling ptn3460_init directly...

Sean


> Thanks,
> Inki Dae
>
>> Sean
>>
>> >
>> >> If it's an i2c driver with the ptn3460_init callback, where it parses
>> >> the dt in probe, then you have a race between the ptn probe and the
>> >> ptn init/drm hooks. ie: it's possible drm will initialize and call
>> >> disable/post_disable/pre_enable/enable before things have been probed.
>> >
>> > And also, exynos drm core will call a probe callback of the sub driver
>> > after _drm is initialized_. Is there something I am missing?
>> >
>> >> To solve this, you need to use some global state and/or locking.
>> >>
>> >> So, to summarize. We can have this of_find_compatible with a init
>> >> callback, or we can have an i2c driver + global state/locking + init
>> >> callback. I chose the former, since it seemed easier.
>> >>
>> >> If you have a better way to achieve this, I'm definitely interested,
>> >> but I saw this as the lesser of two evils.
>> >>
>> >> Sean
>> >>
>> >>> I think you need to implement the lvds-bridge driver as i2c driver,
>> >>> and then consider how to take care of this. I mean let's find a better
>> >>> way how we could take care of the i2c based driver in exynos drm
>> >>> framework or driver. In all cases, such tricky codes are not good.
>> >>>
>> >>>> + if (!bridge->node)
>> >>>> + return false;
>> >>>> +
>> >>>> + bridge->client = of_find_i2c_device_by_node(bridge->node);
>> >>>> + if (!bridge->client)
>> >>>> + return false;
>> >>>> +
>> >>>> + return true;
>> >>>> +}
>> >>>> +
>> >>>> +/* returns the number of bridges attached */
>> >>>> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
>> >>>> + struct drm_encoder *encoder)
>> >>>> +{
>> >>>> + struct bridge_init bridge;
>> >>>> + int ret;
>> >>>> +
>> >>>> + if (find_bridge("nxp,ptn3460", &bridge)) {
>> >>>> + ret = ptn3460_init(dev, encoder, bridge.client,
>> bridge.node);
>> >>>> + if (!ret)
>> >>>> + return 1;
>> >>>> + }
>> >>>> + return 0;
>> >>>> +}
>> >>>> +
>> >>>> static int exynos_drm_create_enc_conn(struct drm_device *dev,
>> >>>> struct exynos_drm_subdrv
> *subdrv)
>> >>>> {
>> >>>> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct
>> drm_device *dev,
>> >>>> DRM_ERROR("failed to create encoder\n");
>> >>>> return -EFAULT;
>> >>>> }
>> >>>> + subdrv->encoder = encoder;
>> >>>> +
>> >>>> + if (subdrv->manager->display_ops->type ==
>> EXYNOS_DISPLAY_TYPE_LCD) {
>> >>>> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
>> >>>> + if (ret)
>> >>>> + return 0;
>> >>>> + }
>> >>>>
>> >>>> /*
>> >>>> * create and initialize a connector for this sub driver and
>> >>>> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct
>> drm_device *dev,
>> >>>> goto err_destroy_encoder;
>> >>>> }
>> >>>>
>> >>>> - subdrv->encoder = encoder;
>> >>>> subdrv->connector = connector;
>> >>>>
>> >>>> return 0;
>> >>>> --
>> >>>> 1.8.4
>> >>>>
>> >>>> --
>> >>>> To unsubscribe from this list: send the line "unsubscribe linux-
>> samsung-soc" in
>> >>>> the body of a message to [email protected]
>> >>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>> >> --
>> >> To unsubscribe from this list: send the line "unsubscribe linux-
>> samsung-soc" in
>> >> the body of a message to [email protected]
>> >> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

Subject: RE: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present



> -----Original Message-----
> From: Sean Paul [mailto:[email protected]]
> Sent: Saturday, October 05, 2013 12:05 AM
> To: Inki Dae
> Cc: Kukjin Kim; DRI mailing list; linux-samsung-soc; Linux ARM Kernel;
> Linux Kernel Mailing List; [email protected];
> [email protected]; Dave Airlie
> Subject: Re: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present
>
> On Fri, Oct 4, 2013 at 11:01 AM, Inki Dae <[email protected]> wrote:
> >
> >
> >> -----Original Message-----
> >> From: Sean Paul [mailto:[email protected]]
> >> Sent: Friday, October 04, 2013 11:17 PM
> >> To: Inki Dae
> >> Cc: Kukjin Kim; DRI mailing list; [email protected];
> >> [email protected]; [email protected];
> linux-
> >> [email protected]; [email protected]; Dave Airlie
> >> Subject: Re: [PATCH v2 4/5] drm/exynos: Initialize ptn3460 if present
> >>
> >> On Fri, Oct 4, 2013 at 12:18 AM, Inki Dae <[email protected]> wrote:
> >> > 2013/10/4 Sean Paul <[email protected]>:
> >> >> On Thu, Oct 3, 2013 at 10:29 PM, Inki Dae <[email protected]>
> wrote:
> >> >>> 2013/10/4 Sean Paul <[email protected]>:
> >> >>>> This patch adds code to look for the ptn3460 in the device tree
> file
> >> on
> >> >>>> exynos initialization. If ptn node is found, the driver will
> >> initialize
> >> >>>> the ptn3460 driver and skip creating a DP connector (since the
> bridge
> >> >>>> driver will register its own connector).
> >> >>>>
> >> >>>> Signed-off-by: Sean Paul <[email protected]>
> >> >>>> ---
> >> >>>>
> >> >>>> v2:
> >> >>>> - Changed include from of_i2c.h to i2c.h
> >> >>>> - Changed of_find_by_name to of_find_compatible
> >> >>>>
> >> >>>> drivers/gpu/drm/exynos/exynos_drm_core.c | 44
> >> +++++++++++++++++++++++++++++++-
> >> >>>> 1 file changed, 43 insertions(+), 1 deletion(-)
> >> >>>>
> >> >>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c
> >> b/drivers/gpu/drm/exynos/exynos_drm_core.c
> >> >>>> index 1bef6dc..08ca4f9 100644
> >> >>>> --- a/drivers/gpu/drm/exynos/exynos_drm_core.c
> >> >>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
> >> >>>> @@ -12,7 +12,9 @@
> >> >>>> * option) any later version.
> >> >>>> */
> >> >>>>
> >> >>>> +#include <linux/i2c.h>
> >> >>>> #include <drm/drmP.h>
> >> >>>> +#include <drm/bridge/ptn3460.h>
> >> >>>> #include "exynos_drm_drv.h"
> >> >>>> #include "exynos_drm_encoder.h"
> >> >>>> #include "exynos_drm_connector.h"
> >> >>>> @@ -20,6 +22,40 @@
> >> >>>>
> >> >>>> static LIST_HEAD(exynos_drm_subdrv_list);
> >> >>>>
> >> >>>> +struct bridge_init {
> >> >>>> + struct i2c_client *client;
> >> >>>> + struct device_node *node;
> >> >>>> +};
> >> >>>> +
> >> >>>> +static bool find_bridge(const char *compat, struct bridge_init
> >> *bridge)
> >> >>>> +{
> >> >>>> + bridge->client = NULL;
> >> >>>> + bridge->node = of_find_compatible_node(NULL, NULL,
compat);
> >> >>>
> >> >>> Then, shouldn't the lvds-bridge driver be implemented as i2c driver
> so
> >> >>> that such tricky isn't needed? Is there any reason you use such
> tricky
> >> >>> codes?
> >> >>>
> >> >>
> >> >> This is what I was explaining earlier today. If the bridge driver is
> >> >> just an i2c driver, it won't get a pointer to drm_device, and can't
> >> >> register itself as a drm_bridge or drm_connector. To solve this, you
> >> >> need the ptn3460_init callback.
> >> >>
> >> >
> >> > No, I think you can use sub driver. how about registering to exynos
> >> > drm core that driver using exynos_drm_subdrv_register function after
> >> > probed? Is there something I am missing?
> >> >
> >>
> >> The ptn driver isn't exynos-specific, so I don't think making it an
> >> exynos_drm_subdrv is appropriate.
> >>
> >
> > I _really know_ that the ptn driver isn't exynos-specific. I mean that
> you
> > can use exynos_drm_subdrv somehow. ie. you can add a new bridge module
> > specific to exynos, and this module can register its own sub driver at
> end
> > of probe. Why do you try to use such tricky codes?
> >
>
> So I create a new exynos_lvds_driver which is an i2c driver. When that
> probes, all it does is register that driver as an exynos_drm_subdrv.
> Then in the subdrv probe I call into ptn3460_init?
>
> That seems a lot more tricky than just calling ptn3460_init directly...
>

Why more tricky? At least the dt binding will be done in device driver.

Anyway, this also is reasonable to me. It seems that we need a bit different
design for such bridge driver.

Basically, exysnos drm fimd driver has one encoder and one connector, and
these are connected each other, and this connector means lcd panel without
any display bus. So if we want to use display bus, it might need to create a
new encoder and connector for the display bus device. It seems that this way
is more reasonable to me.
ie. if we want for image data goes from fimd to lcd panel, we can connect a
existing connector and crtc of fimd, and if fimd to display bus, we can
connect the new connector and the crtc of fimd through setcrtc or setplane.

Of course, for this we would need more works and efforts. Such tricky codes
definitely are not good.

Thanks,
Inki Dae

> Sean
>
>
> > Thanks,
> > Inki Dae
> >
> >> Sean
> >>
> >> >
> >> >> If it's an i2c driver with the ptn3460_init callback, where it
> parses
> >> >> the dt in probe, then you have a race between the ptn probe and the
> >> >> ptn init/drm hooks. ie: it's possible drm will initialize and call
> >> >> disable/post_disable/pre_enable/enable before things have been
> probed.
> >> >
> >> > And also, exynos drm core will call a probe callback of the sub
> driver
> >> > after _drm is initialized_. Is there something I am missing?
> >> >
> >> >> To solve this, you need to use some global state and/or locking.
> >> >>
> >> >> So, to summarize. We can have this of_find_compatible with a init
> >> >> callback, or we can have an i2c driver + global state/locking + init
> >> >> callback. I chose the former, since it seemed easier.
> >> >>
> >> >> If you have a better way to achieve this, I'm definitely interested,
> >> >> but I saw this as the lesser of two evils.
> >> >>
> >> >> Sean
> >> >>
> >> >>> I think you need to implement the lvds-bridge driver as i2c driver,
> >> >>> and then consider how to take care of this. I mean let's find a
> better
> >> >>> way how we could take care of the i2c based driver in exynos drm
> >> >>> framework or driver. In all cases, such tricky codes are not good.
> >> >>>
> >> >>>> + if (!bridge->node)
> >> >>>> + return false;
> >> >>>> +
> >> >>>> + bridge->client = of_find_i2c_device_by_node(bridge->node);
> >> >>>> + if (!bridge->client)
> >> >>>> + return false;
> >> >>>> +
> >> >>>> + return true;
> >> >>>> +}
> >> >>>> +
> >> >>>> +/* returns the number of bridges attached */
> >> >>>> +static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
> >> >>>> + struct drm_encoder *encoder)
> >> >>>> +{
> >> >>>> + struct bridge_init bridge;
> >> >>>> + int ret;
> >> >>>> +
> >> >>>> + if (find_bridge("nxp,ptn3460", &bridge)) {
> >> >>>> + ret = ptn3460_init(dev, encoder, bridge.client,
> >> bridge.node);
> >> >>>> + if (!ret)
> >> >>>> + return 1;
> >> >>>> + }
> >> >>>> + return 0;
> >> >>>> +}
> >> >>>> +
> >> >>>> static int exynos_drm_create_enc_conn(struct drm_device *dev,
> >> >>>> struct exynos_drm_subdrv
> > *subdrv)
> >> >>>> {
> >> >>>> @@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct
> >> drm_device *dev,
> >> >>>> DRM_ERROR("failed to create encoder\n");
> >> >>>> return -EFAULT;
> >> >>>> }
> >> >>>> + subdrv->encoder = encoder;
> >> >>>> +
> >> >>>> + if (subdrv->manager->display_ops->type ==
> >> EXYNOS_DISPLAY_TYPE_LCD) {
> >> >>>> + ret = exynos_drm_attach_lcd_bridge(dev, encoder);
> >> >>>> + if (ret)
> >> >>>> + return 0;
> >> >>>> + }
> >> >>>>
> >> >>>> /*
> >> >>>> * create and initialize a connector for this sub driver
and
> >> >>>> @@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct
> >> drm_device *dev,
> >> >>>> goto err_destroy_encoder;
> >> >>>> }
> >> >>>>
> >> >>>> - subdrv->encoder = encoder;
> >> >>>> subdrv->connector = connector;
> >> >>>>
> >> >>>> return 0;
> >> >>>> --
> >> >>>> 1.8.4
> >> >>>>
> >> >>>> --
> >> >>>> To unsubscribe from this list: send the line "unsubscribe linux-
> >> samsung-soc" in
> >> >>>> the body of a message to [email protected]
> >> >>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> >> >> --
> >> >> To unsubscribe from this list: send the line "unsubscribe linux-
> >> samsung-soc" in
> >> >> the body of a message to [email protected]
> >> >> More majordomo info at http://vger.kernel.org/majordomo-info.html
> >

2013-10-09 18:29:43

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

On Fri, Oct 04, 2013 at 11:05:48AM +0900, Inki Dae wrote:
> 2013/10/4 Olof Johansson <[email protected]>:

> > "If PD_N is LOW, then the device is in Deep power-down completely,
> > even if supply rail is ON; for the device to be able to operate, the
> > PD_N pin must be HIGH."

> I still think the pin could be replaced with a regulator. But
> lvds-bridge node has "powerdown-gpio" property - it say this board
> will use gpio pin - specific to board. So it seems no problem.

No, don't model things that aren't regulators as regulators - it's just
confusing from a usability standpoint and causes breakage when the pins
don't behave like regulators.


Attachments:
(No filename) (651.00 B)
signature.asc (836.00 B)
Digital signature
Download all attachments
Subject: RE: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver



> -----Original Message-----
> From: Mark Brown [mailto:[email protected]]
> Sent: Thursday, October 10, 2013 3:29 AM
> To: Inki Dae
> Cc: Olof Johansson; Sean Paul; [email protected]; linux-samsung-
> [email protected]; [email protected]; linux-
> [email protected]; DRI mailing list; linux-arm-
> [email protected]
> Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver
>
> On Fri, Oct 04, 2013 at 11:05:48AM +0900, Inki Dae wrote:
> > 2013/10/4 Olof Johansson <[email protected]>:
>
> > > "If PD_N is LOW, then the device is in Deep power-down completely,
> > > even if supply rail is ON; for the device to be able to operate, the
> > > PD_N pin must be HIGH."
>
> > I still think the pin could be replaced with a regulator. But
> > lvds-bridge node has "powerdown-gpio" property - it say this board
> > will use gpio pin - specific to board. So it seems no problem.
>
> No, don't model things that aren't regulators as regulators - it's just
> confusing from a usability standpoint and causes breakage when the pins
> don't behave like regulators.

It seems that there was your missing point. That _is not_ what I mentioned.
I mean that other boards can use a regulator instead of gpio pin.



2013-10-10 09:37:45

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

On Thu, Oct 10, 2013 at 01:18:05PM +0900, Inki Dae wrote:

> > > I still think the pin could be replaced with a regulator. But
> > > lvds-bridge node has "powerdown-gpio" property - it say this board
> > > will use gpio pin - specific to board. So it seems no problem.

> > No, don't model things that aren't regulators as regulators - it's just
> > confusing from a usability standpoint and causes breakage when the pins
> > don't behave like regulators.

> It seems that there was your missing point. That _is not_ what I mentioned.
> I mean that other boards can use a regulator instead of gpio pin.

What I'm saying is no boards should use a regulator to control that
GPIO pin, obviously if they're controlling the actual regulators that's
fine but the reset signal should not be controlled via the regulator
API (there are some unfortunate cases where people have done that
already but let's not have any more).


Attachments:
(No filename) (918.00 B)
signature.asc (836.00 B)
Digital signature
Download all attachments
Subject: RE: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver



> -----Original Message-----
> From: Mark Brown [mailto:[email protected]]
> Sent: Thursday, October 10, 2013 6:37 PM
> To: Inki Dae
> Cc: 'Olof Johansson'; 'Sean Paul'; [email protected]; linux-
> [email protected]; [email protected]; linux-
> [email protected]; 'DRI mailing list'; linux-arm-
> [email protected]
> Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver
>
> On Thu, Oct 10, 2013 at 01:18:05PM +0900, Inki Dae wrote:
>
> > > > I still think the pin could be replaced with a regulator. But
> > > > lvds-bridge node has "powerdown-gpio" property - it say this board
> > > > will use gpio pin - specific to board. So it seems no problem.
>
> > > No, don't model things that aren't regulators as regulators - it's
> > > just confusing from a usability standpoint and causes breakage when
> > > the pins don't behave like regulators.
>
> > It seems that there was your missing point. That _is not_ what I
> mentioned.
> > I mean that other boards can use a regulator instead of gpio pin.
>
> What I'm saying is no boards should use a regulator to control that GPIO
> pin, obviously if they're controlling the actual regulators that's fine

That is what I mentioned. Some boards _could control_ the actual regulator
for lvds-bridge, and that would be depended on how HW engineer designs the
board.

> but the reset signal should not be controlled via the regulator API (there
> are some unfortunate cases where people have done that already but let's
> not have any more).

2013-10-10 12:24:09

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 3/5] drm/bridge: Add PTN3460 bridge driver

On Thu, Oct 10, 2013 at 08:40:38PM +0900, Inki Dae wrote:

> That is what I mentioned. Some boards _could control_ the actual regulator
> for lvds-bridge, and that would be depended on how HW engineer designs the
> board.

For the driver this should be totally transparent - it should just
control the regulator all the time, the regulator API will just not do
anything if the regulator state can't actually be changed.


Attachments:
(No filename) (421.00 B)
signature.asc (836.00 B)
Digital signature
Download all attachments

2013-10-10 22:26:37

by Sean Paul

[permalink] [raw]
Subject: [PATCH v3 0/5] Add some missing bits for exynos5250-snow

This set adds some missing devicetree nodes to the exynos5250-snow file as well
as adds a drm_bridge driver for the ptn3460 DP-LVDS chip. This chip is used in
the exynos5250-snow board.

Sean


Sean Paul (5):
ARM: dts: Add fimd display-timings for exynos5250-snow
ARM: dts: Add dp-controller node to exynos5250-snow
drm/bridge: Add PTN3460 bridge driver
drm/exynos: Initialize ptn3460 if present
ARM: dts: Add ptn3460 to exynos5250-snow

Documentation/devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++++++++++++++++
arch/arm/boot/dts/exynos5250-snow.dts | 50 ++++++++++++++++
drivers/gpu/drm/Kconfig | 2 ++
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/bridge/Kconfig | 4 +++
drivers/gpu/drm/bridge/Makefile | 3 ++
drivers/gpu/drm/bridge/ptn3460.c | 349 ++++++++++++++++
drivers/gpu/drm/exynos/exynos_drm_core.c | 44 ++++++++++++++++
include/drm/bridge/ptn3460.h | 37 ++++++++++++++++

2013-10-10 22:26:43

by Sean Paul

[permalink] [raw]
Subject: [PATCH v3 2/5] ARM: dts: Add dp-controller node to exynos5250-snow

This patch adds the dp-controller node to the exynos5250-snow board dts
file.

Signed-off-by: Sean Paul <[email protected]>
---

v2: Added dp-controller address to node (rebased on linux-next)
v3: Added status = "okay"

arch/arm/boot/dts/exynos5250-snow.dts | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index 28eea9b..f813644 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -186,6 +186,19 @@
samsung,vbus-gpio = <&gpx1 1 0>;
};

+ dp-controller@145B0000 {
+ samsung,color-space = <0>;
+ samsung,dynamic-range = <0>;
+ samsung,ycbcr-coeff = <0>;
+ samsung,color-depth = <1>;
+ samsung,link-rate = <0x0a>;
+ samsung,lane-count = <2>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&dp_hpd>;
+ status = "okay";
+ };
+
fimd: fimd@14400000 {
status = "okay";
display-timings {
--
1.8.4

2013-10-10 22:26:50

by Sean Paul

[permalink] [raw]
Subject: [PATCH v3 5/5] ARM: dts: Add ptn3460 to exynos5250-snow

This patch adds a node for the ptn3460 DP-LVDS chip in the
exynos5250-snow board dts file.

Signed-off-by: Sean Paul <[email protected]>
---

v2: Changed node name to lvds-bridge
v3: No changes

arch/arm/boot/dts/exynos5250-snow.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index f813644..8c92df8 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -33,6 +33,13 @@
sd3_bus4: sd3-bus-width4 {
samsung,pin-drv = <0>;
};
+
+ ptn3460_gpios: ptn3460-gpios {
+ samsung,pins = "gpy2-5", "gpx1-5";
+ samsung,pin-function = <1>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
};

gpio-keys {
@@ -186,6 +193,18 @@
samsung,vbus-gpio = <&gpx1 1 0>;
};

+ i2c@12CD0000 {
+ lvds-bridge@20 {
+ compatible = "nxp,ptn3460";
+ reg = <0x20>;
+ powerdown-gpio = <&gpy2 5 0>;
+ reset-gpio = <&gpx1 5 0>;
+ edid-emulation = <5>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&ptn3460_gpios>;
+ };
+ };
+
dp-controller@145B0000 {
samsung,color-space = <0>;
samsung,dynamic-range = <0>;
--
1.8.4

2013-10-10 22:27:14

by Sean Paul

[permalink] [raw]
Subject: [PATCH v3 4/5] drm/exynos: Initialize ptn3460 if present

This patch adds code to look for the ptn3460 in the device tree file on
exynos initialization. If ptn node is found, the driver will initialize
the ptn3460 driver and skip creating a DP connector (since the bridge
driver will register its own connector).

Signed-off-by: Sean Paul <[email protected]>
---

v2:
- Changed include from of_i2c.h to i2c.h
- Changed of_find_by_name to of_find_compatible
v3: No changes


drivers/gpu/drm/exynos/exynos_drm_core.c | 44 +++++++++++++++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 1bef6dc..08ca4f9 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -12,7 +12,9 @@
* option) any later version.
*/

+#include <linux/i2c.h>
#include <drm/drmP.h>
+#include <drm/bridge/ptn3460.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_connector.h"
@@ -20,6 +22,40 @@

static LIST_HEAD(exynos_drm_subdrv_list);

+struct bridge_init {
+ struct i2c_client *client;
+ struct device_node *node;
+};
+
+static bool find_bridge(const char *compat, struct bridge_init *bridge)
+{
+ bridge->client = NULL;
+ bridge->node = of_find_compatible_node(NULL, NULL, compat);
+ if (!bridge->node)
+ return false;
+
+ bridge->client = of_find_i2c_device_by_node(bridge->node);
+ if (!bridge->client)
+ return false;
+
+ return true;
+}
+
+/* returns the number of bridges attached */
+static int exynos_drm_attach_lcd_bridge(struct drm_device *dev,
+ struct drm_encoder *encoder)
+{
+ struct bridge_init bridge;
+ int ret;
+
+ if (find_bridge("nxp,ptn3460", &bridge)) {
+ ret = ptn3460_init(dev, encoder, bridge.client, bridge.node);
+ if (!ret)
+ return 1;
+ }
+ return 0;
+}
+
static int exynos_drm_create_enc_conn(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
@@ -36,6 +72,13 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
DRM_ERROR("failed to create encoder\n");
return -EFAULT;
}
+ subdrv->encoder = encoder;
+
+ if (subdrv->manager->display_ops->type == EXYNOS_DISPLAY_TYPE_LCD) {
+ ret = exynos_drm_attach_lcd_bridge(dev, encoder);
+ if (ret)
+ return 0;
+ }

/*
* create and initialize a connector for this sub driver and
@@ -48,7 +91,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev,
goto err_destroy_encoder;
}

- subdrv->encoder = encoder;
subdrv->connector = connector;

return 0;
--
1.8.4

2013-10-10 22:27:34

by Sean Paul

[permalink] [raw]
Subject: [PATCH v3 3/5] drm/bridge: Add PTN3460 bridge driver

This patch adds a drm_bridge driver for the PTN3460 DisplayPort to LVDS
bridge chip.

Signed-off-by: Sean Paul <[email protected]>
---

v2:
- Changed header definition to static inline
- Changed dt node name to lvds-bridge
v3: No changes


.../devicetree/bindings/drm/bridge/ptn3460.txt | 27 ++
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/bridge/Kconfig | 4 +
drivers/gpu/drm/bridge/Makefile | 3 +
drivers/gpu/drm/bridge/ptn3460.c | 349 +++++++++++++++++++++
include/drm/bridge/ptn3460.h | 37 +++
7 files changed, 423 insertions(+)
create mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
create mode 100644 drivers/gpu/drm/bridge/Kconfig
create mode 100644 drivers/gpu/drm/bridge/Makefile
create mode 100644 drivers/gpu/drm/bridge/ptn3460.c
create mode 100644 include/drm/bridge/ptn3460.h

diff --git a/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
new file mode 100644
index 0000000..52b93b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/bridge/ptn3460.txt
@@ -0,0 +1,27 @@
+ptn3460 bridge bindings
+
+Required properties:
+ - compatible: "nxp,ptn3460"
+ - reg: i2c address of the bridge
+ - powerdown-gpio: OF device-tree gpio specification
+ - reset-gpio: OF device-tree gpio specification
+ - edid-emulation: The EDID emulation entry to use
+ +-------+------------+------------------+
+ | Value | Resolution | Description |
+ | 0 | 1024x768 | NXP Generic |
+ | 1 | 1920x1080 | NXP Generic |
+ | 2 | 1920x1080 | NXP Generic |
+ | 3 | 1600x900 | Samsung LTM200KT |
+ | 4 | 1920x1080 | Samsung LTM230HT |
+ | 5 | 1366x768 | NXP Generic |
+ | 6 | 1600x900 | ChiMei M215HGE |
+ +-------+------------+------------------+
+
+Example:
+ lvds-bridge@20 {
+ compatible = "nxp,ptn3460";
+ reg = <0x20>;
+ powerdown-gpio = <&gpy2 5 1 0 0>;
+ reset-gpio = <&gpx1 5 1 0 0>;
+ edid-emulation = <5>;
+ };
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 955555d..cd7bfb3 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
source "drivers/gpu/drm/qxl/Kconfig"

source "drivers/gpu/drm/msm/Kconfig"
+
+source "drivers/gpu/drm/bridge/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index f089adf..9234253 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -56,3 +56,4 @@ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_MSM) += msm/
obj-y += i2c/
+obj-y += bridge/
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
new file mode 100644
index 0000000..f8db069
--- /dev/null
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -0,0 +1,4 @@
+config DRM_PTN3460
+ tristate "PTN3460 DP/LVDS bridge"
+ depends on DRM && I2C
+ ---help---
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
new file mode 100644
index 0000000..b4733e1
--- /dev/null
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -0,0 +1,3 @@
+ccflags-y := -Iinclude/drm
+
+obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
new file mode 100644
index 0000000..a9e5c1a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ptn3460.c
@@ -0,0 +1,349 @@
+/*
+ * NXP PTN3460 DP/LVDS bridge driver
+ *
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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/of.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "drmP.h"
+#include "drm_edid.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+#include "bridge/ptn3460.h"
+
+#define PTN3460_EDID_ADDR 0x0
+#define PTN3460_EDID_EMULATION_ADDR 0x84
+#define PTN3460_EDID_ENABLE_EMULATION 0
+#define PTN3460_EDID_EMULATION_SELECTION 1
+#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85
+
+struct ptn3460_bridge {
+ struct drm_connector connector;
+ struct i2c_client *client;
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+ struct edid *edid;
+ int gpio_pd_n;
+ int gpio_rst_n;
+ u32 edid_emulation;
+ bool enabled;
+};
+
+static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr,
+ u8 *buf, int len)
+{
+ int ret;
+
+ ret = i2c_master_send(ptn_bridge->client, &addr, 1);
+ if (ret <= 0) {
+ DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = i2c_master_recv(ptn_bridge->client, buf, len);
+ if (ret <= 0) {
+ DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr,
+ char val)
+{
+ int ret;
+ char buf[2];
+
+ buf[0] = addr;
+ buf[1] = val;
+
+ ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf));
+ if (ret <= 0) {
+ DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
+{
+ int ret;
+ char val;
+
+ /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */
+ ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR,
+ ptn_bridge->edid_emulation);
+ if (ret) {
+ DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret);
+ return ret;
+ }
+
+ /* Enable EDID emulation and select the desired EDID */
+ val = 1 << PTN3460_EDID_ENABLE_EMULATION |
+ ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION;
+
+ ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val);
+ if (ret) {
+ DRM_ERROR("Failed to write edid value, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ptn3460_pre_enable(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+ int ret;
+
+ if (ptn_bridge->enabled)
+ return;
+
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_set_value(ptn_bridge->gpio_pd_n, 1);
+
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
+ gpio_set_value(ptn_bridge->gpio_rst_n, 0);
+ udelay(10);
+ gpio_set_value(ptn_bridge->gpio_rst_n, 1);
+ }
+
+ /*
+ * There's a bug in the PTN chip where it falsely asserts hotplug before
+ * it is fully functional. We're forced to wait for the maximum start up
+ * time specified in the chip's datasheet to make sure we're really up.
+ */
+ msleep(90);
+
+ ret = ptn3460_select_edid(ptn_bridge);
+ if (ret)
+ DRM_ERROR("Select edid failed ret=%d\n", ret);
+
+ ptn_bridge->enabled = true;
+}
+
+static void ptn3460_enable(struct drm_bridge *bridge)
+{
+}
+
+static void ptn3460_disable(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+
+ if (!ptn_bridge->enabled)
+ return;
+
+ ptn_bridge->enabled = false;
+
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_set_value(ptn_bridge->gpio_rst_n, 1);
+
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_set_value(ptn_bridge->gpio_pd_n, 0);
+}
+
+static void ptn3460_post_disable(struct drm_bridge *bridge)
+{
+}
+
+void ptn3460_bridge_destroy(struct drm_bridge *bridge)
+{
+ struct ptn3460_bridge *ptn_bridge = bridge->driver_private;
+
+ drm_bridge_cleanup(bridge);
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_free(ptn_bridge->gpio_pd_n);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_free(ptn_bridge->gpio_rst_n);
+ /* Nothing else to free, we've got devm allocated memory */
+}
+
+struct drm_bridge_funcs ptn3460_bridge_funcs = {
+ .pre_enable = ptn3460_pre_enable,
+ .enable = ptn3460_enable,
+ .disable = ptn3460_disable,
+ .post_disable = ptn3460_post_disable,
+ .destroy = ptn3460_bridge_destroy,
+};
+
+int ptn3460_get_modes(struct drm_connector *connector)
+{
+ struct ptn3460_bridge *ptn_bridge;
+ u8 *edid;
+ int ret, num_modes;
+ bool power_off;
+
+ ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
+
+ if (ptn_bridge->edid)
+ return drm_add_edid_modes(connector, ptn_bridge->edid);
+
+ power_off = !ptn_bridge->enabled;
+ ptn3460_pre_enable(ptn_bridge->bridge);
+
+ edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!edid) {
+ DRM_ERROR("Failed to allocate edid\n");
+ return 0;
+ }
+
+ ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid,
+ EDID_LENGTH);
+ if (ret) {
+ kfree(edid);
+ num_modes = 0;
+ goto out;
+ }
+
+ ptn_bridge->edid = (struct edid *)edid;
+ drm_mode_connector_update_edid_property(connector, ptn_bridge->edid);
+
+ num_modes = drm_add_edid_modes(connector, ptn_bridge->edid);
+
+out:
+ if (power_off)
+ ptn3460_disable(ptn_bridge->bridge);
+
+ return num_modes;
+}
+
+static int ptn3460_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
+{
+ struct ptn3460_bridge *ptn_bridge;
+
+ ptn_bridge = container_of(connector, struct ptn3460_bridge, connector);
+
+ return ptn_bridge->encoder;
+}
+
+struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
+ .get_modes = ptn3460_get_modes,
+ .mode_valid = ptn3460_mode_valid,
+ .best_encoder = ptn3460_best_encoder,
+};
+
+enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
+ bool force)
+{
+ return connector_status_connected;
+}
+
+void ptn3460_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_cleanup(connector);
+}
+
+struct drm_connector_funcs ptn3460_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = ptn3460_detect,
+ .destroy = ptn3460_connector_destroy,
+};
+
+int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
+ struct i2c_client *client, struct device_node *node)
+{
+ int ret;
+ struct drm_bridge *bridge;
+ struct ptn3460_bridge *ptn_bridge;
+
+ bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
+ if (!bridge) {
+ DRM_ERROR("Failed to allocate drm bridge\n");
+ return -ENOMEM;
+ }
+
+ ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL);
+ if (!ptn_bridge) {
+ DRM_ERROR("Failed to allocate ptn bridge\n");
+ return -ENOMEM;
+ }
+
+ ptn_bridge->client = client;
+ ptn_bridge->encoder = encoder;
+ ptn_bridge->bridge = bridge;
+ ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0);
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n)) {
+ ret = gpio_request_one(ptn_bridge->gpio_pd_n,
+ GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N");
+ if (ret) {
+ DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n)) {
+ /*
+ * Request the reset pin low to avoid the bridge being
+ * initialized prematurely
+ */
+ ret = gpio_request_one(ptn_bridge->gpio_rst_n,
+ GPIOF_OUT_INIT_LOW, "PTN3460_RST_N");
+ if (ret) {
+ DRM_ERROR("Request reset-gpio failed (%d)\n", ret);
+ gpio_free(ptn_bridge->gpio_pd_n);
+ return ret;
+ }
+ }
+
+ ret = of_property_read_u32(node, "edid-emulation",
+ &ptn_bridge->edid_emulation);
+ if (ret) {
+ DRM_ERROR("Can't read edid emulation value\n");
+ goto err;
+ }
+
+ ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs);
+ if (ret) {
+ DRM_ERROR("Failed to initialize bridge with drm\n");
+ goto err;
+ }
+
+ bridge->driver_private = ptn_bridge;
+ encoder->bridge = bridge;
+
+ ret = drm_connector_init(dev, &ptn_bridge->connector,
+ &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ goto err;
+ }
+ drm_connector_helper_add(&ptn_bridge->connector,
+ &ptn3460_connector_helper_funcs);
+ drm_sysfs_connector_add(&ptn_bridge->connector);
+ drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
+
+ return 0;
+
+err:
+ if (gpio_is_valid(ptn_bridge->gpio_pd_n))
+ gpio_free(ptn_bridge->gpio_pd_n);
+ if (gpio_is_valid(ptn_bridge->gpio_rst_n))
+ gpio_free(ptn_bridge->gpio_rst_n);
+ return ret;
+}
diff --git a/include/drm/bridge/ptn3460.h b/include/drm/bridge/ptn3460.h
new file mode 100644
index 0000000..8481816
--- /dev/null
+++ b/include/drm/bridge/ptn3460.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 Google, Inc.
+ *
+ * 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 _DRM_BRIDGE_PTN3460_H_
+#define _DRM_BRIDGE_PTN3460_H_
+
+struct drm_device;
+struct drm_encoder;
+struct i2c_client;
+struct device_node;
+
+#ifdef CONFIG_DRM_PTN3460
+
+int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
+ struct i2c_client *client, struct device_node *node);
+#else
+
+static inline int ptn3460_init(struct drm_device *dev,
+ struct drm_encoder *encoder, struct i2c_client *client,
+ struct device_node *node)
+{
+ return 0;
+}
+
+#endif
+
+#endif
--
1.8.4

2013-10-10 22:27:51

by Sean Paul

[permalink] [raw]
Subject: [PATCH v3 1/5] ARM: dts: Add fimd display-timings for exynos5250-snow

This patch adds the internal panel timings to the exynos5250-snow board
dts file.

Signed-off-by: Sean Paul <[email protected]>
---

v2: No difference
v3: Added status = "okay"

arch/arm/boot/dts/exynos5250-snow.dts | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index fd711e2..28eea9b 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -186,6 +186,24 @@
samsung,vbus-gpio = <&gpx1 1 0>;
};

+ fimd: fimd@14400000 {
+ status = "okay";
+ display-timings {
+ native-mode = <&lcd_timing>;
+ lcd_timing: 1366x768 {
+ clock-frequency = <70589280>;
+ hactive = <1366>;
+ vactive = <768>;
+ hfront-porch = <40>;
+ hback-porch = <40>;
+ hsync-len = <32>;
+ vback-porch = <10>;
+ vfront-porch = <12>;
+ vsync-len = <6>;
+ };
+ };
+ };
+
fixed-rate-clocks {
xxti {
compatible = "samsung,clock-xxti";
--
1.8.4