mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties
@ 2015-11-02  2:09 Jitao Shi
  2015-11-02  2:09 ` [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge Jitao Shi
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Jitao Shi @ 2015-11-02  2:09 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	David Airlie, Matthias Brugger
  Cc: Jitao Shi, Thierry Reding, Ajay Kumar, Inki Dae, Rahul Sharma,
	Sean Paul, Vincent Palatin, Andy Yan, Philipp Zabel,
	Russell King, devicetree, linux-kernel, dri-devel,
	linux-arm-kernel, linux-mediatek, srv_heupstream, Sascha Hauer,
	yingjoe.chen, eddie.huang, cawa.cheng, bibby.hsieh

Add documentation for DT properties supported by
ps8640 DSI-eDP converter.

Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
---
 .../devicetree/bindings/display/bridge/ps8640.txt  |   43 ++++++++++++++++++++
 1 file changed, 43 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/ps8640.txt

diff --git a/Documentation/devicetree/bindings/display/bridge/ps8640.txt b/Documentation/devicetree/bindings/display/bridge/ps8640.txt
new file mode 100644
index 0000000..7edc547
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/ps8640.txt
@@ -0,0 +1,43 @@
+ps8640-bridge bindings
+
+Required properties:
+	- compatible: "parade,ps8640"
+	- reg: first page address of the bridge.
+	- sleep-gpios: OF device-tree gpio specification for PD_ pin.
+	- reset-gpios: OF device-tree gpio specification for reset pin.
+	- mode-sel-gpios: OF device-tree gpio specification for mode-sel pin.
+	- vdd12-supply: OF device-tree regulator specification for 1.2V power.
+	- vdd33-supply: OF device-tree regulator specification for 3.3V power.
+	- ports: The device node can contain video interface port nodes per
+		 the video-interfaces bind[1]. For port@0,set the reg = <0> as
+		 ps8640 dsi in and port@1,set the reg = <1> as ps8640 eDP out.
+
+[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
+
+Example:
+	edp-bridge@18 {
+		compatible = "parade,ps8640";
+		reg = <0x18>;
+		sleep-gpios = <&pio 116 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&pio 115 GPIO_ACTIVE_HIGH>;
+		mode-sel-gpios = <&pio 92 GPIO_ACTIVE_HIGH>;
+		ps8640-1v2-supply = <&ps8640_fixed_1v2>;
+		ps8640-3v3-supply = <&mt6397_vgp2_reg>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			port@0 {
+				reg = <0>;
+				ps8640_in: endpoint {
+					remote-endpoint = <&dsi0_out>;
+				};
+			};
+			port@1 {
+				reg = <1>;
+				ps8640_out: endpoint {
+					remote-endpoint = <&panel_in>;
+				};
+			};
+		};
+	};
-- 
1.7.9.5


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

* [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge
  2015-11-02  2:09 [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties Jitao Shi
@ 2015-11-02  2:09 ` Jitao Shi
  2015-11-02  3:54   ` jitao shi
  2015-11-05  9:09   ` Philipp Zabel
  2015-11-02  3:53 ` [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties jitao shi
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 11+ messages in thread
From: Jitao Shi @ 2015-11-02  2:09 UTC (permalink / raw)
  To: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	David Airlie, Matthias Brugger
  Cc: Jitao Shi, Thierry Reding, Ajay Kumar, Inki Dae, Rahul Sharma,
	Sean Paul, Vincent Palatin, Andy Yan, Philipp Zabel,
	Russell King, devicetree, linux-kernel, dri-devel,
	linux-arm-kernel, linux-mediatek, srv_heupstream, Sascha Hauer,
	yingjoe.chen, eddie.huang, cawa.cheng, bibby.hsieh

This patch adds drm_bridge driver for parade DSI to eDP bridge chip.

Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
---
 drivers/gpu/drm/bridge/Kconfig         |    9 +
 drivers/gpu/drm/bridge/Makefile        |    1 +
 drivers/gpu/drm/bridge/parade-ps8640.c |  479 ++++++++++++++++++++++++++++++++
 3 files changed, 489 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/parade-ps8640.c

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 2de52a5..8ecaeed 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -29,4 +29,13 @@ config DRM_PARADE_PS8622
 	---help---
 	  Parade eDP-LVDS bridge chip driver.
 
+config DRM_PARADE_PS8640
+	bool "Parade PS8640 MIPI DSI to eDP Converter"
+	select DRM_KMS_HELPER
+	select DRM_PANEL
+	---help---
+	  Choose this option if you have PS8640 for display
+	  The PS8640 is a high-performance and low-power
+	  MIPI DSI to eDP converter
+
 endmenu
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index e2eef1c..da9e4a4 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
+obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
new file mode 100644
index 0000000..52616ba
--- /dev/null
+++ b/drivers/gpu/drm/bridge/parade-ps8640.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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 <linux/of_graph.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_atomic_helper.h>
+
+#define PAGE2_GPIO_L	0xa6
+#define PAGE2_GPIO_H	0xa7
+#define PS_GPIO9	BIT(1)
+
+#define PAGE4_REV_L	0xf0
+#define PAGE4_REV_H	0xf1
+#define PAGE4_CHIP_L	0xf2
+#define PAGE4_CHIP_H	0xf3
+
+#define bridge_to_ps8640(e)	container_of(e, struct ps8640, bridge)
+#define connector_to_ps8640(e)	container_of(e, struct ps8640, connector)
+
+struct ps8640 {
+	struct drm_connector connector;
+	struct drm_bridge bridge;
+	struct i2c_client *client;
+	struct ps8640_driver_data *driver_data;
+	struct regulator *pwr_1v2_supply;
+	struct regulator *pwr_3v3_supply;
+	struct drm_panel *panel;
+	struct gpio_desc *gpio_rst_n;
+	struct gpio_desc *gpio_slp_n;
+	struct gpio_desc *gpio_mode_sel_n;
+	u16 base_reg;
+	bool enabled;
+};
+
+static int ps8640_regr(struct i2c_client *client, u16 i2c_addr,
+		       u8 reg, u8 *value)
+{
+	int ret;
+
+	client->addr = i2c_addr;
+
+	ret = i2c_master_send(client, &reg, 1);
+	if (ret <= 0) {
+		DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
+		return ret;
+	}
+
+	ret = i2c_master_recv(client, value, 1);
+	if (ret <= 0) {
+		DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ps8640_regw(struct i2c_client *client, u16 i2c_addr,
+		       u8 reg, u8 value)
+{
+	int ret;
+	char buf[2];
+
+	client->addr = i2c_addr;
+
+	buf[0] = reg;
+	buf[1] = value;
+	ret = i2c_master_send(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 ps8640_check_valid_id(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->client;
+	u8 rev_id_low, rev_id_high, chip_id_low, chip_id_high;
+	int retry_cnt = 0;
+
+	do {
+		ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_CHIP_H,
+			    &chip_id_high);
+		if (chip_id_high != 0x30)
+			DRM_INFO("chip_id_high = 0x%x\n", chip_id_high);
+	} while ((retry_cnt++ < 2) && (chip_id_high != 0x30));
+
+	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_REV_L, &rev_id_low);
+	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_REV_H, &rev_id_high);
+	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_CHIP_L,
+		    &chip_id_low);
+
+	if ((rev_id_low == 0x00) && (rev_id_high == 0x0a) &&
+	    (chip_id_low == 0x00) && (chip_id_high == 0x30))
+		return 1;
+
+	return 0;
+}
+
+static void ps8640_show_mcu_fw_version(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->client;
+	u8 major_ver, minor_ver;
+
+	ps8640_regr(client, ps_bridge->base_reg + 5, 0x4, &major_ver);
+	ps8640_regr(client, ps_bridge->base_reg + 5, 0x5, &minor_ver);
+
+	DRM_INFO_ONCE("ps8640 rom fw version %d.%d\n", major_ver, minor_ver);
+}
+
+static int ps8640_bdg_enable(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->client;
+
+	if (ps8640_check_valid_id(ps_bridge)) {
+		ps8640_regw(client, ps_bridge->base_reg + 3, 0xfe, 0x13);
+		ps8640_regw(client, ps_bridge->base_reg + 3, 0xff, 0x18);
+		ps8640_regw(client, ps_bridge->base_reg + 3, 0xfe, 0x13);
+		ps8640_regw(client, ps_bridge->base_reg + 3, 0xff, 0x1c);
+
+		return 0;
+	}
+
+	return -1;
+}
+
+static void ps8640_prepare(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->client;
+	int err, retry_cnt = 0;
+	u8 set_vdo_done;
+
+	if (ps_bridge->enabled)
+		return;
+
+	if (drm_panel_prepare(ps_bridge->panel)) {
+		DRM_ERROR("failed to prepare panel\n");
+		return;
+	}
+
+	err = regulator_enable(ps_bridge->pwr_1v2_supply);
+	if (err < 0) {
+		DRM_ERROR("failed to enable pwr_1v2_supply: %d\n", err);
+		return;
+	}
+
+	err = regulator_enable(ps_bridge->pwr_3v3_supply);
+	if (err < 0) {
+		DRM_ERROR("failed to enable pwr_3v3_supply: %d\n", err);
+		return;
+	}
+
+	gpiod_set_value(ps_bridge->gpio_slp_n, 1);
+	gpiod_set_value(ps_bridge->gpio_rst_n, 0);
+	usleep_range(500, 700);
+	gpiod_set_value(ps_bridge->gpio_rst_n, 1);
+
+	do {
+		msleep(50);
+		ps8640_regr(client, ps_bridge->base_reg + 2, PAGE2_GPIO_H,
+			    &set_vdo_done);
+	} while ((retry_cnt++ < 70) && ((set_vdo_done & PS_GPIO9) != PS_GPIO9));
+
+	ps8640_show_mcu_fw_version(ps_bridge);
+	ps_bridge->enabled = true;
+}
+
+static void ps8640_pre_enable(struct drm_bridge *bridge)
+{
+	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+
+	ps8640_prepare(ps_bridge);
+}
+
+static void ps8640_enable(struct drm_bridge *bridge)
+{
+	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+
+	ps8640_bdg_enable(ps_bridge);
+
+	if (drm_panel_enable(ps_bridge->panel)) {
+		DRM_ERROR("failed to enable panel\n");
+		return;
+	}
+}
+
+static void ps8640_disable(struct drm_bridge *bridge)
+{
+	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+
+	if (!ps_bridge->enabled)
+		return;
+
+	ps_bridge->enabled = false;
+
+	if (drm_panel_disable(ps_bridge->panel)) {
+		DRM_ERROR("failed to disable panel\n");
+		return;
+	}
+
+	regulator_disable(ps_bridge->pwr_1v2_supply);
+	regulator_disable(ps_bridge->pwr_3v3_supply);
+	gpiod_set_value(ps_bridge->gpio_rst_n, 0);
+	gpiod_set_value(ps_bridge->gpio_slp_n, 0);
+}
+
+static void ps8640_post_disable(struct drm_bridge *bridge)
+{
+	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+
+	if (drm_panel_unprepare(ps_bridge->panel)) {
+		DRM_ERROR("failed to unprepare panel\n");
+		return;
+	}
+}
+
+static int ps8640_get_modes(struct drm_connector *connector)
+{
+	struct ps8640 *ps_bridge = connector_to_ps8640(connector);
+	struct i2c_client *client = ps_bridge->client;
+
+	ps8640_prepare(ps_bridge);
+	ps8640_regw(client, ps_bridge->base_reg + 2, 0xea, 0xd0);
+
+	return drm_panel_get_modes(ps_bridge->panel);
+}
+
+static struct drm_encoder *ps8640_best_encoder(struct drm_connector *connector)
+{
+	struct ps8640 *ps_bridge;
+
+	ps_bridge = connector_to_ps8640(connector);
+	return ps_bridge->bridge.encoder;
+}
+
+static const struct drm_connector_helper_funcs
+	ps8640_connector_helper_funcs = {
+	.get_modes = ps8640_get_modes,
+	.best_encoder = ps8640_best_encoder,
+};
+
+static enum drm_connector_status ps8640_detect(struct drm_connector *connector,
+					       bool force)
+{
+	return connector_status_connected;
+}
+
+static void ps8640_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs ps8640_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = ps8640_detect,
+	.destroy = ps8640_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+int ps8640_bridge_attach(struct drm_bridge *bridge)
+{
+	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+	int ret;
+
+	if (!bridge->encoder) {
+		DRM_ERROR("Parent encoder object not found");
+		return -ENODEV;
+	}
+
+	ret = drm_connector_init(bridge->dev, &ps_bridge->connector,
+				 &ps8640_connector_funcs,
+				 DRM_MODE_CONNECTOR_eDP);
+
+	if (ret) {
+		DRM_ERROR("Failed to initialize connector with drm\n");
+		return ret;
+	}
+
+	drm_connector_helper_add(&ps_bridge->connector,
+				 &ps8640_connector_helper_funcs);
+	drm_connector_register(&ps_bridge->connector);
+
+	ps_bridge->connector.dpms = DRM_MODE_DPMS_ON;
+	drm_mode_connector_attach_encoder(&ps_bridge->connector,
+					  bridge->encoder);
+
+	if (ps_bridge->panel)
+		drm_panel_attach(ps_bridge->panel, &ps_bridge->connector);
+
+	return ret;
+}
+
+static bool ps8640_bridge_mode_fixup(struct drm_bridge *bridge,
+				     const struct drm_display_mode *mode,
+				     struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static const struct drm_bridge_funcs ps8640_bridge_funcs = {
+	.attach = ps8640_bridge_attach,
+	.mode_fixup = ps8640_bridge_mode_fixup,
+	.disable = ps8640_disable,
+	.post_disable = ps8640_post_disable,
+	.pre_enable = ps8640_pre_enable,
+	.enable = ps8640_enable,
+};
+
+static int ps8640_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct ps8640 *ps_bridge;
+	struct device_node *np = dev->of_node;
+	struct device_node *in_ep, *out_ep;
+	struct device_node *panel_node = NULL;
+	int ret;
+	u32 temp_reg;
+
+	ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL);
+	if (!ps_bridge)
+		return -ENOMEM;
+
+	in_ep = of_graph_get_next_endpoint(np, NULL);
+	if (in_ep) {
+		out_ep = of_graph_get_next_endpoint(np, in_ep);
+		of_node_put(in_ep);
+		if (out_ep) {
+			panel_node = of_graph_get_remote_port_parent(out_ep);
+			of_node_put(out_ep);
+		}
+	}
+	if (panel_node) {
+		ps_bridge->panel = of_drm_find_panel(panel_node);
+		of_node_put(panel_node);
+		if (!ps_bridge->panel)
+			return -EPROBE_DEFER;
+	}
+
+	ps_bridge->client = client;
+
+	ps_bridge->pwr_3v3_supply = devm_regulator_get(dev, "vdd33-supply");
+	if (IS_ERR(ps_bridge->pwr_3v3_supply)) {
+		dev_err(dev, "cannot get ps_bridge->pwr_3v3_supply\n");
+		return PTR_ERR(ps_bridge->pwr_3v3_supply);
+	}
+
+	ps_bridge->pwr_1v2_supply = devm_regulator_get(dev, "vdd12-supply");
+	if (IS_ERR(ps_bridge->pwr_1v2_supply)) {
+		dev_err(dev, "cannot get ps_bridge->pwr_1v2_supply\n");
+		return PTR_ERR(ps_bridge->pwr_1v2_supply);
+	}
+
+	ps_bridge->gpio_mode_sel_n = devm_gpiod_get(&client->dev, "mode-sel",
+						    GPIOD_OUT_HIGH);
+	if (IS_ERR(ps_bridge->gpio_mode_sel_n)) {
+		ret = PTR_ERR(ps_bridge->gpio_mode_sel_n);
+		DRM_ERROR("cannot get gpio_mode_sel_n %d\n", ret);
+		return ret;
+	}
+
+	ret = gpiod_direction_output(ps_bridge->gpio_mode_sel_n, 1);
+	if (ret) {
+		DRM_ERROR("cannot configure gpio_mode_sel_n\n");
+		return ret;
+	}
+
+	ps_bridge->gpio_slp_n = devm_gpiod_get(&client->dev, "sleep-gpios",
+					       GPIOD_OUT_HIGH);
+	if (IS_ERR(ps_bridge->gpio_slp_n)) {
+		ret = PTR_ERR(ps_bridge->gpio_slp_n);
+		DRM_ERROR("cannot get gpio_slp_n %d\n", ret);
+		return ret;
+	}
+
+	ret = gpiod_direction_output(ps_bridge->gpio_slp_n, 1);
+	if (ret) {
+		DRM_ERROR("cannot configure gpio_slp_n\n");
+		return ret;
+	}
+
+	ps_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset",
+					       GPIOD_OUT_HIGH);
+	if (IS_ERR(ps_bridge->gpio_rst_n)) {
+		ret = PTR_ERR(ps_bridge->gpio_rst_n);
+		DRM_ERROR("cannot get gpio_rst_n %d\n", ret);
+		return ret;
+	}
+
+	ret = gpiod_direction_output(ps_bridge->gpio_rst_n, 1);
+	if (ret) {
+		DRM_ERROR("cannot configure gpio_rst_n\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "reg", &temp_reg);
+	if (ret) {
+		DRM_ERROR("Can't read base_reg value\n");
+		return ret;
+	}
+	ps_bridge->base_reg = temp_reg;
+
+	ps_bridge->bridge.funcs = &ps8640_bridge_funcs;
+	ps_bridge->bridge.of_node = dev->of_node;
+	ret = drm_bridge_add(&ps_bridge->bridge);
+	if (ret) {
+		DRM_ERROR("Failed to add bridge\n");
+		return ret;
+	}
+
+	i2c_set_clientdata(client, ps_bridge);
+
+	return 0;
+}
+
+static int ps8640_remove(struct i2c_client *client)
+{
+	struct ps8640 *ps_bridge = i2c_get_clientdata(client);
+
+	drm_bridge_remove(&ps_bridge->bridge);
+
+	return 0;
+}
+
+static const struct i2c_device_id ps8640_i2c_table[] = {
+	{"parade,ps8640", 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, ps8640_i2c_table);
+
+static const struct of_device_id ps8640_match[] = {
+	{ .compatible = "parade,ps8640" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ps8640_match);
+
+static struct i2c_driver ps8640_driver = {
+	.id_table = ps8640_i2c_table,
+	.probe = ps8640_probe,
+	.remove = ps8640_remove,
+	.driver = {
+		.name = "parade,ps8640",
+		.owner = THIS_MODULE,
+		.of_match_table = ps8640_match,
+	},
+};
+module_i2c_driver(ps8640_driver);
+
+MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
+MODULE_AUTHOR("CK Hu <ck.hu@mediatek.com>");
+MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5


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

* Re: [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties
  2015-11-02  2:09 [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties Jitao Shi
  2015-11-02  2:09 ` [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge Jitao Shi
@ 2015-11-02  3:53 ` jitao shi
  2015-11-02 11:09   ` Philipp Zabel
  2015-11-02 11:35 ` Philipp Zabel
  2015-11-06  2:02 ` Rob Herring
  3 siblings, 1 reply; 11+ messages in thread
From: jitao shi @ 2015-11-02  3:53 UTC (permalink / raw)
  To: Rob Herring, Ian Campbell, Kumar Gala, David Airlie,
	Matthias Brugger, Thierry Reding, Ajay Kumar, Inki Dae,
	Rahul Sharma, Sean Paul, Vincent Palatin, Andy Yan,
	Philipp Zabel, Russell King, devicetree, linux-kernel, dri-devel,
	linux-arm-kernel, linux-mediatek, srv_heupstream, Sascha Hauer,
	yingjoe.chen, eddie.huang, cawa.cheng, bibby.hsieh, Pawel Moll,
	Mark Rutland
  Cc: ck.hu

On Mon, 2015-11-02 at 10:09 +0800, Jitao Shi wrote:
> Add documentation for DT properties supported by
> ps8640 DSI-eDP converter.
> 
> Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
---
changes since v1:
        1. changes power-gpios to sleep-gpios.
        2. drop "ps8640-" in properties.
        3. add ports property.
> ---
>  .../devicetree/bindings/display/bridge/ps8640.txt  |   43 ++++++++++++++++++++
>  1 file changed, 43 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/bridge/ps8640.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/bridge/ps8640.txt b/Documentation/devicetree/bindings/display/bridge/ps8640.txt
> new file mode 100644
> index 0000000..7edc547
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/bridge/ps8640.txt
> @@ -0,0 +1,43 @@
> +ps8640-bridge bindings
> +
> +Required properties:
> +	- compatible: "parade,ps8640"
> +	- reg: first page address of the bridge.
> +	- sleep-gpios: OF device-tree gpio specification for PD_ pin.
> +	- reset-gpios: OF device-tree gpio specification for reset pin.
> +	- mode-sel-gpios: OF device-tree gpio specification for mode-sel pin.
> +	- vdd12-supply: OF device-tree regulator specification for 1.2V power.
> +	- vdd33-supply: OF device-tree regulator specification for 3.3V power.
> +	- ports: The device node can contain video interface port nodes per
> +		 the video-interfaces bind[1]. For port@0,set the reg = <0> as
> +		 ps8640 dsi in and port@1,set the reg = <1> as ps8640 eDP out.
> +
> +[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
> +
> +Example:
> +	edp-bridge@18 {
> +		compatible = "parade,ps8640";
> +		reg = <0x18>;
> +		sleep-gpios = <&pio 116 GPIO_ACTIVE_HIGH>;
> +		reset-gpios = <&pio 115 GPIO_ACTIVE_HIGH>;
> +		mode-sel-gpios = <&pio 92 GPIO_ACTIVE_HIGH>;
> +		ps8640-1v2-supply = <&ps8640_fixed_1v2>;
> +		ps8640-3v3-supply = <&mt6397_vgp2_reg>;
> +
> +		ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			port@0 {
> +				reg = <0>;
> +				ps8640_in: endpoint {
> +					remote-endpoint = <&dsi0_out>;
> +				};
> +			};
> +			port@1 {
> +				reg = <1>;
> +				ps8640_out: endpoint {
> +					remote-endpoint = <&panel_in>;
> +				};
> +			};
> +		};
> +	};



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

* Re: [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge
  2015-11-02  2:09 ` [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge Jitao Shi
@ 2015-11-02  3:54   ` jitao shi
  2015-11-02 11:34     ` Philipp Zabel
  2015-11-05  9:09   ` Philipp Zabel
  1 sibling, 1 reply; 11+ messages in thread
From: jitao shi @ 2015-11-02  3:54 UTC (permalink / raw)
  To: Rob Herring, Ian Campbell, Kumar Gala, David Airlie,
	Matthias Brugger, Thierry Reding, Ajay Kumar, Inki Dae,
	Rahul Sharma, Sean Paul, Vincent Palatin, Andy Yan,
	Philipp Zabel, Russell King, devicetree, linux-kernel, dri-devel,
	linux-arm-kernel, linux-mediatek, srv_heupstream, Sascha Hauer,
	yingjoe.chen, eddie.huang, cawa.cheng, bibby.hsieh, Pawel Moll,
	Mark Rutland
  Cc: ck.hu

On Mon, 2015-11-02 at 10:09 +0800, Jitao Shi wrote:
> This patch adds drm_bridge driver for parade DSI to eDP bridge chip.
> 
> Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
---
changes since v1:
        1. remove edid code
        2. remove some wrong comments
        3. tune some gpios name, make it easy understand the meaning
> ---
>  drivers/gpu/drm/bridge/Kconfig         |    9 +
>  drivers/gpu/drm/bridge/Makefile        |    1 +
>  drivers/gpu/drm/bridge/parade-ps8640.c |  479 ++++++++++++++++++++++++++++++++
>  3 files changed, 489 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/parade-ps8640.c
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 2de52a5..8ecaeed 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -29,4 +29,13 @@ config DRM_PARADE_PS8622
>  	---help---
>  	  Parade eDP-LVDS bridge chip driver.
>  
> +config DRM_PARADE_PS8640
> +	bool "Parade PS8640 MIPI DSI to eDP Converter"
> +	select DRM_KMS_HELPER
> +	select DRM_PANEL
> +	---help---
> +	  Choose this option if you have PS8640 for display
> +	  The PS8640 is a high-performance and low-power
> +	  MIPI DSI to eDP converter
> +
>  endmenu
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index e2eef1c..da9e4a4 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm
>  obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
>  obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
>  obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
> +obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
> diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
> new file mode 100644
> index 0000000..52616ba
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/parade-ps8640.c
> @@ -0,0 +1,479 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * 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 <linux/of_graph.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_atomic_helper.h>
> +
> +#define PAGE2_GPIO_L	0xa6
> +#define PAGE2_GPIO_H	0xa7
> +#define PS_GPIO9	BIT(1)
> +
> +#define PAGE4_REV_L	0xf0
> +#define PAGE4_REV_H	0xf1
> +#define PAGE4_CHIP_L	0xf2
> +#define PAGE4_CHIP_H	0xf3
> +
> +#define bridge_to_ps8640(e)	container_of(e, struct ps8640, bridge)
> +#define connector_to_ps8640(e)	container_of(e, struct ps8640, connector)
> +
> +struct ps8640 {
> +	struct drm_connector connector;
> +	struct drm_bridge bridge;
> +	struct i2c_client *client;
> +	struct ps8640_driver_data *driver_data;
> +	struct regulator *pwr_1v2_supply;
> +	struct regulator *pwr_3v3_supply;
> +	struct drm_panel *panel;
> +	struct gpio_desc *gpio_rst_n;
> +	struct gpio_desc *gpio_slp_n;
> +	struct gpio_desc *gpio_mode_sel_n;
> +	u16 base_reg;
> +	bool enabled;
> +};
> +
> +static int ps8640_regr(struct i2c_client *client, u16 i2c_addr,
> +		       u8 reg, u8 *value)
> +{
> +	int ret;
> +
> +	client->addr = i2c_addr;
> +
> +	ret = i2c_master_send(client, &reg, 1);
> +	if (ret <= 0) {
> +		DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = i2c_master_recv(client, value, 1);
> +	if (ret <= 0) {
> +		DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ps8640_regw(struct i2c_client *client, u16 i2c_addr,
> +		       u8 reg, u8 value)
> +{
> +	int ret;
> +	char buf[2];
> +
> +	client->addr = i2c_addr;
> +
> +	buf[0] = reg;
> +	buf[1] = value;
> +	ret = i2c_master_send(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 ps8640_check_valid_id(struct ps8640 *ps_bridge)
> +{
> +	struct i2c_client *client = ps_bridge->client;
> +	u8 rev_id_low, rev_id_high, chip_id_low, chip_id_high;
> +	int retry_cnt = 0;
> +
> +	do {
> +		ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_CHIP_H,
> +			    &chip_id_high);
> +		if (chip_id_high != 0x30)
> +			DRM_INFO("chip_id_high = 0x%x\n", chip_id_high);
> +	} while ((retry_cnt++ < 2) && (chip_id_high != 0x30));
> +
> +	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_REV_L, &rev_id_low);
> +	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_REV_H, &rev_id_high);
> +	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_CHIP_L,
> +		    &chip_id_low);
> +
> +	if ((rev_id_low == 0x00) && (rev_id_high == 0x0a) &&
> +	    (chip_id_low == 0x00) && (chip_id_high == 0x30))
> +		return 1;
> +
> +	return 0;
> +}
> +
> +static void ps8640_show_mcu_fw_version(struct ps8640 *ps_bridge)
> +{
> +	struct i2c_client *client = ps_bridge->client;
> +	u8 major_ver, minor_ver;
> +
> +	ps8640_regr(client, ps_bridge->base_reg + 5, 0x4, &major_ver);
> +	ps8640_regr(client, ps_bridge->base_reg + 5, 0x5, &minor_ver);
> +
> +	DRM_INFO_ONCE("ps8640 rom fw version %d.%d\n", major_ver, minor_ver);
> +}
> +
> +static int ps8640_bdg_enable(struct ps8640 *ps_bridge)
> +{
> +	struct i2c_client *client = ps_bridge->client;
> +
> +	if (ps8640_check_valid_id(ps_bridge)) {
> +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xfe, 0x13);
> +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xff, 0x18);
> +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xfe, 0x13);
> +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xff, 0x1c);
> +
> +		return 0;
> +	}
> +
> +	return -1;
> +}
> +
> +static void ps8640_prepare(struct ps8640 *ps_bridge)
> +{
> +	struct i2c_client *client = ps_bridge->client;
> +	int err, retry_cnt = 0;
> +	u8 set_vdo_done;
> +
> +	if (ps_bridge->enabled)
> +		return;
> +
> +	if (drm_panel_prepare(ps_bridge->panel)) {
> +		DRM_ERROR("failed to prepare panel\n");
> +		return;
> +	}
> +
> +	err = regulator_enable(ps_bridge->pwr_1v2_supply);
> +	if (err < 0) {
> +		DRM_ERROR("failed to enable pwr_1v2_supply: %d\n", err);
> +		return;
> +	}
> +
> +	err = regulator_enable(ps_bridge->pwr_3v3_supply);
> +	if (err < 0) {
> +		DRM_ERROR("failed to enable pwr_3v3_supply: %d\n", err);
> +		return;
> +	}
> +
> +	gpiod_set_value(ps_bridge->gpio_slp_n, 1);
> +	gpiod_set_value(ps_bridge->gpio_rst_n, 0);
> +	usleep_range(500, 700);
> +	gpiod_set_value(ps_bridge->gpio_rst_n, 1);
> +
> +	do {
> +		msleep(50);
> +		ps8640_regr(client, ps_bridge->base_reg + 2, PAGE2_GPIO_H,
> +			    &set_vdo_done);
> +	} while ((retry_cnt++ < 70) && ((set_vdo_done & PS_GPIO9) != PS_GPIO9));
> +
> +	ps8640_show_mcu_fw_version(ps_bridge);
> +	ps_bridge->enabled = true;
> +}
> +
> +static void ps8640_pre_enable(struct drm_bridge *bridge)
> +{
> +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> +
> +	ps8640_prepare(ps_bridge);
> +}
> +
> +static void ps8640_enable(struct drm_bridge *bridge)
> +{
> +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> +
> +	ps8640_bdg_enable(ps_bridge);
> +
> +	if (drm_panel_enable(ps_bridge->panel)) {
> +		DRM_ERROR("failed to enable panel\n");
> +		return;
> +	}
> +}
> +
> +static void ps8640_disable(struct drm_bridge *bridge)
> +{
> +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> +
> +	if (!ps_bridge->enabled)
> +		return;
> +
> +	ps_bridge->enabled = false;
> +
> +	if (drm_panel_disable(ps_bridge->panel)) {
> +		DRM_ERROR("failed to disable panel\n");
> +		return;
> +	}
> +
> +	regulator_disable(ps_bridge->pwr_1v2_supply);
> +	regulator_disable(ps_bridge->pwr_3v3_supply);
> +	gpiod_set_value(ps_bridge->gpio_rst_n, 0);
> +	gpiod_set_value(ps_bridge->gpio_slp_n, 0);
> +}
> +
> +static void ps8640_post_disable(struct drm_bridge *bridge)
> +{
> +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> +
> +	if (drm_panel_unprepare(ps_bridge->panel)) {
> +		DRM_ERROR("failed to unprepare panel\n");
> +		return;
> +	}
> +}
> +
> +static int ps8640_get_modes(struct drm_connector *connector)
> +{
> +	struct ps8640 *ps_bridge = connector_to_ps8640(connector);
> +	struct i2c_client *client = ps_bridge->client;
> +
> +	ps8640_prepare(ps_bridge);
> +	ps8640_regw(client, ps_bridge->base_reg + 2, 0xea, 0xd0);
> +
> +	return drm_panel_get_modes(ps_bridge->panel);
> +}
> +
> +static struct drm_encoder *ps8640_best_encoder(struct drm_connector *connector)
> +{
> +	struct ps8640 *ps_bridge;
> +
> +	ps_bridge = connector_to_ps8640(connector);
> +	return ps_bridge->bridge.encoder;
> +}
> +
> +static const struct drm_connector_helper_funcs
> +	ps8640_connector_helper_funcs = {
> +	.get_modes = ps8640_get_modes,
> +	.best_encoder = ps8640_best_encoder,
> +};
> +
> +static enum drm_connector_status ps8640_detect(struct drm_connector *connector,
> +					       bool force)
> +{
> +	return connector_status_connected;
> +}
> +
> +static void ps8640_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +static const struct drm_connector_funcs ps8640_connector_funcs = {
> +	.dpms = drm_atomic_helper_connector_dpms,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.detect = ps8640_detect,
> +	.destroy = ps8640_connector_destroy,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +int ps8640_bridge_attach(struct drm_bridge *bridge)
> +{
> +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> +	int ret;
> +
> +	if (!bridge->encoder) {
> +		DRM_ERROR("Parent encoder object not found");
> +		return -ENODEV;
> +	}
> +
> +	ret = drm_connector_init(bridge->dev, &ps_bridge->connector,
> +				 &ps8640_connector_funcs,
> +				 DRM_MODE_CONNECTOR_eDP);
> +
> +	if (ret) {
> +		DRM_ERROR("Failed to initialize connector with drm\n");
> +		return ret;
> +	}
> +
> +	drm_connector_helper_add(&ps_bridge->connector,
> +				 &ps8640_connector_helper_funcs);
> +	drm_connector_register(&ps_bridge->connector);
> +
> +	ps_bridge->connector.dpms = DRM_MODE_DPMS_ON;
> +	drm_mode_connector_attach_encoder(&ps_bridge->connector,
> +					  bridge->encoder);
> +
> +	if (ps_bridge->panel)
> +		drm_panel_attach(ps_bridge->panel, &ps_bridge->connector);
> +
> +	return ret;
> +}
> +
> +static bool ps8640_bridge_mode_fixup(struct drm_bridge *bridge,
> +				     const struct drm_display_mode *mode,
> +				     struct drm_display_mode *adjusted_mode)
> +{
> +	return true;
> +}
> +
> +static const struct drm_bridge_funcs ps8640_bridge_funcs = {
> +	.attach = ps8640_bridge_attach,
> +	.mode_fixup = ps8640_bridge_mode_fixup,
> +	.disable = ps8640_disable,
> +	.post_disable = ps8640_post_disable,
> +	.pre_enable = ps8640_pre_enable,
> +	.enable = ps8640_enable,
> +};
> +
> +static int ps8640_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct device *dev = &client->dev;
> +	struct ps8640 *ps_bridge;
> +	struct device_node *np = dev->of_node;
> +	struct device_node *in_ep, *out_ep;
> +	struct device_node *panel_node = NULL;
> +	int ret;
> +	u32 temp_reg;
> +
> +	ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL);
> +	if (!ps_bridge)
> +		return -ENOMEM;
> +
> +	in_ep = of_graph_get_next_endpoint(np, NULL);
> +	if (in_ep) {
> +		out_ep = of_graph_get_next_endpoint(np, in_ep);
> +		of_node_put(in_ep);
> +		if (out_ep) {
> +			panel_node = of_graph_get_remote_port_parent(out_ep);
> +			of_node_put(out_ep);
> +		}
> +	}
> +	if (panel_node) {
> +		ps_bridge->panel = of_drm_find_panel(panel_node);
> +		of_node_put(panel_node);
> +		if (!ps_bridge->panel)
> +			return -EPROBE_DEFER;
> +	}
> +
> +	ps_bridge->client = client;
> +
> +	ps_bridge->pwr_3v3_supply = devm_regulator_get(dev, "vdd33-supply");
> +	if (IS_ERR(ps_bridge->pwr_3v3_supply)) {
> +		dev_err(dev, "cannot get ps_bridge->pwr_3v3_supply\n");
> +		return PTR_ERR(ps_bridge->pwr_3v3_supply);
> +	}
> +
> +	ps_bridge->pwr_1v2_supply = devm_regulator_get(dev, "vdd12-supply");
> +	if (IS_ERR(ps_bridge->pwr_1v2_supply)) {
> +		dev_err(dev, "cannot get ps_bridge->pwr_1v2_supply\n");
> +		return PTR_ERR(ps_bridge->pwr_1v2_supply);
> +	}
> +
> +	ps_bridge->gpio_mode_sel_n = devm_gpiod_get(&client->dev, "mode-sel",
> +						    GPIOD_OUT_HIGH);
> +	if (IS_ERR(ps_bridge->gpio_mode_sel_n)) {
> +		ret = PTR_ERR(ps_bridge->gpio_mode_sel_n);
> +		DRM_ERROR("cannot get gpio_mode_sel_n %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = gpiod_direction_output(ps_bridge->gpio_mode_sel_n, 1);
> +	if (ret) {
> +		DRM_ERROR("cannot configure gpio_mode_sel_n\n");
> +		return ret;
> +	}
> +
> +	ps_bridge->gpio_slp_n = devm_gpiod_get(&client->dev, "sleep-gpios",
> +					       GPIOD_OUT_HIGH);
> +	if (IS_ERR(ps_bridge->gpio_slp_n)) {
> +		ret = PTR_ERR(ps_bridge->gpio_slp_n);
> +		DRM_ERROR("cannot get gpio_slp_n %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = gpiod_direction_output(ps_bridge->gpio_slp_n, 1);
> +	if (ret) {
> +		DRM_ERROR("cannot configure gpio_slp_n\n");
> +		return ret;
> +	}
> +
> +	ps_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset",
> +					       GPIOD_OUT_HIGH);
> +	if (IS_ERR(ps_bridge->gpio_rst_n)) {
> +		ret = PTR_ERR(ps_bridge->gpio_rst_n);
> +		DRM_ERROR("cannot get gpio_rst_n %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = gpiod_direction_output(ps_bridge->gpio_rst_n, 1);
> +	if (ret) {
> +		DRM_ERROR("cannot configure gpio_rst_n\n");
> +		return ret;
> +	}
> +
> +	ret = of_property_read_u32(dev->of_node, "reg", &temp_reg);
> +	if (ret) {
> +		DRM_ERROR("Can't read base_reg value\n");
> +		return ret;
> +	}
> +	ps_bridge->base_reg = temp_reg;
> +
> +	ps_bridge->bridge.funcs = &ps8640_bridge_funcs;
> +	ps_bridge->bridge.of_node = dev->of_node;
> +	ret = drm_bridge_add(&ps_bridge->bridge);
> +	if (ret) {
> +		DRM_ERROR("Failed to add bridge\n");
> +		return ret;
> +	}
> +
> +	i2c_set_clientdata(client, ps_bridge);
> +
> +	return 0;
> +}
> +
> +static int ps8640_remove(struct i2c_client *client)
> +{
> +	struct ps8640 *ps_bridge = i2c_get_clientdata(client);
> +
> +	drm_bridge_remove(&ps_bridge->bridge);
> +
> +	return 0;
> +}
> +
> +static const struct i2c_device_id ps8640_i2c_table[] = {
> +	{"parade,ps8640", 0},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(i2c, ps8640_i2c_table);
> +
> +static const struct of_device_id ps8640_match[] = {
> +	{ .compatible = "parade,ps8640" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, ps8640_match);
> +
> +static struct i2c_driver ps8640_driver = {
> +	.id_table = ps8640_i2c_table,
> +	.probe = ps8640_probe,
> +	.remove = ps8640_remove,
> +	.driver = {
> +		.name = "parade,ps8640",
> +		.owner = THIS_MODULE,
> +		.of_match_table = ps8640_match,
> +	},
> +};
> +module_i2c_driver(ps8640_driver);
> +
> +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> +MODULE_AUTHOR("CK Hu <ck.hu@mediatek.com>");
> +MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver");
> +MODULE_LICENSE("GPL v2");



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

* Re: [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties
  2015-11-02  3:53 ` [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties jitao shi
@ 2015-11-02 11:09   ` Philipp Zabel
  2015-11-05  1:15     ` jitao shi
  0 siblings, 1 reply; 11+ messages in thread
From: Philipp Zabel @ 2015-11-02 11:09 UTC (permalink / raw)
  To: jitao shi
  Cc: Rob Herring, Ian Campbell, Kumar Gala, David Airlie,
	Matthias Brugger, Thierry Reding, Ajay Kumar, Inki Dae,
	Rahul Sharma, Sean Paul, Vincent Palatin, Andy Yan, Russell King,
	devicetree, linux-kernel, dri-devel, linux-arm-kernel,
	linux-mediatek, srv_heupstream, Sascha Hauer, yingjoe.chen,
	eddie.huang, cawa.cheng, bibby.hsieh, Pawel Moll, Mark Rutland,
	ck.hu

Hi Jitao,

Am Montag, den 02.11.2015, 11:53 +0800 schrieb jitao shi:
[...]
> > +Example:
> > +	edp-bridge@18 {
> > +		compatible = "parade,ps8640";
> > +		reg = <0x18>;
> > +		sleep-gpios = <&pio 116 GPIO_ACTIVE_HIGH>;
> > +		reset-gpios = <&pio 115 GPIO_ACTIVE_HIGH>;
> > +		mode-sel-gpios = <&pio 92 GPIO_ACTIVE_HIGH>;
> > +		ps8640-1v2-supply = <&ps8640_fixed_1v2>;

This should be vdd12-supply now.

> > +		ps8640-3v3-supply = <&mt6397_vgp2_reg>;

Should be vdd33-supply now.

regards
Philipp


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

* Re: [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge
  2015-11-02  3:54   ` jitao shi
@ 2015-11-02 11:34     ` Philipp Zabel
  2015-11-05  1:22       ` jitao shi
  0 siblings, 1 reply; 11+ messages in thread
From: Philipp Zabel @ 2015-11-02 11:34 UTC (permalink / raw)
  To: jitao shi
  Cc: Rob Herring, Ian Campbell, Kumar Gala, David Airlie,
	Matthias Brugger, Thierry Reding, Ajay Kumar, Inki Dae,
	Rahul Sharma, Sean Paul, Vincent Palatin, Andy Yan, Russell King,
	devicetree, linux-kernel, dri-devel, linux-arm-kernel,
	linux-mediatek, srv_heupstream, Sascha Hauer, yingjoe.chen,
	eddie.huang, cawa.cheng, bibby.hsieh, Pawel Moll, Mark Rutland,
	ck.hu

Hi Jitao,

a few comments below.

Am Montag, den 02.11.2015, 11:54 +0800 schrieb jitao shi:
[...]
> > +static int ps8640_check_valid_id(struct ps8640 *ps_bridge)

This could be bool and return true/false.

> > +{
> > +	struct i2c_client *client = ps_bridge->client;
> > +	u8 rev_id_low, rev_id_high, chip_id_low, chip_id_high;
> > +	int retry_cnt = 0;
> > +
> > +	do {
> > +		ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_CHIP_H,
> > +			    &chip_id_high);
> > +		if (chip_id_high != 0x30)
> > +			DRM_INFO("chip_id_high = 0x%x\n", chip_id_high);
> > +	} while ((retry_cnt++ < 2) && (chip_id_high != 0x30));
> > +
> > +	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_REV_L, &rev_id_low);
> > +	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_REV_H, &rev_id_high);
> > +	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_CHIP_L,
> > +		    &chip_id_low);
> > +
> > +	if ((rev_id_low == 0x00) && (rev_id_high == 0x0a) &&
> > +	    (chip_id_low == 0x00) && (chip_id_high == 0x30))
> > +		return 1;
> > +
> > +	return 0;
> > +}
> > +
> > +static void ps8640_show_mcu_fw_version(struct ps8640 *ps_bridge)
> > +{
> > +	struct i2c_client *client = ps_bridge->client;
> > +	u8 major_ver, minor_ver;
> > +
> > +	ps8640_regr(client, ps_bridge->base_reg + 5, 0x4, &major_ver);
> > +	ps8640_regr(client, ps_bridge->base_reg + 5, 0x5, &minor_ver);
> > +
> > +	DRM_INFO_ONCE("ps8640 rom fw version %d.%d\n", major_ver, minor_ver);
> > +}
> > +
> > +static int ps8640_bdg_enable(struct ps8640 *ps_bridge)
> > +{
> > +	struct i2c_client *client = ps_bridge->client;
> > +
> > +	if (ps8640_check_valid_id(ps_bridge)) {
> > +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xfe, 0x13);
> > +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xff, 0x18);
> > +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xfe, 0x13);
> > +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xff, 0x1c);

Can you introduce #defines with descriptive names for those magic
constants and register offsets, and maybe also i2c address offsets?

> > +
> > +		return 0;
> > +	}
> > +
> > +	return -1;

Never return -1 to signal a problem.
If there was an error, use a proper error code.

> > +}
> > +
> > +static void ps8640_prepare(struct ps8640 *ps_bridge)
> > +{
> > +	struct i2c_client *client = ps_bridge->client;
> > +	int err, retry_cnt = 0;
> > +	u8 set_vdo_done;
> > +
> > +	if (ps_bridge->enabled)
> > +		return;
> > +
> > +	if (drm_panel_prepare(ps_bridge->panel)) {
> > +		DRM_ERROR("failed to prepare panel\n");
> > +		return;
> > +	}
> > +
> > +	err = regulator_enable(ps_bridge->pwr_1v2_supply);
> > +	if (err < 0) {
> > +		DRM_ERROR("failed to enable pwr_1v2_supply: %d\n", err);

Missing panel unprepare.

> > +		return;
> > +	}
> > +
> > +	err = regulator_enable(ps_bridge->pwr_3v3_supply);
> > +	if (err < 0) {
> > +		DRM_ERROR("failed to enable pwr_3v3_supply: %d\n", err);

Missing panel unprepare and vdd12 regulator disable.

> > +		return;
> > +	}
> > +
> > +	gpiod_set_value(ps_bridge->gpio_slp_n, 1);
> > +	gpiod_set_value(ps_bridge->gpio_rst_n, 0);
> > +	usleep_range(500, 700);
> > +	gpiod_set_value(ps_bridge->gpio_rst_n, 1);
> > +
> > +	do {
> > +		msleep(50);
> > +		ps8640_regr(client, ps_bridge->base_reg + 2, PAGE2_GPIO_H,
> > +			    &set_vdo_done);
> > +	} while ((retry_cnt++ < 70) && ((set_vdo_done & PS_GPIO9) != PS_GPIO9));
> > +
> > +	ps8640_show_mcu_fw_version(ps_bridge);
> > +	ps_bridge->enabled = true;
> > +}
> > +
> > +static void ps8640_pre_enable(struct drm_bridge *bridge)
> > +{
> > +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> > +
> > +	ps8640_prepare(ps_bridge);
> > +}
> > +
> > +static void ps8640_enable(struct drm_bridge *bridge)
> > +{
> > +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> > +
> > +	ps8640_bdg_enable(ps_bridge);
> > +
> > +	if (drm_panel_enable(ps_bridge->panel)) {
> > +		DRM_ERROR("failed to enable panel\n");
> > +		return;

The return is superfluous.

> > +	}
> > +}
> > +
> > +static void ps8640_disable(struct drm_bridge *bridge)
> > +{
> > +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> > +
> > +	if (!ps_bridge->enabled)
> > +		return;
> > +
> > +	ps_bridge->enabled = false;
> > +
> > +	if (drm_panel_disable(ps_bridge->panel)) {
> > +		DRM_ERROR("failed to disable panel\n");
> > +		return;

Shouldn't we still disable the bridge, even if panel disable fails?

> > +	}
> > +
> > +	regulator_disable(ps_bridge->pwr_1v2_supply);
> > +	regulator_disable(ps_bridge->pwr_3v3_supply);
> > +	gpiod_set_value(ps_bridge->gpio_rst_n, 0);
> > +	gpiod_set_value(ps_bridge->gpio_slp_n, 0);
> > +}
> > +
> > +static void ps8640_post_disable(struct drm_bridge *bridge)
> > +{
> > +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> > +
> > +	if (drm_panel_unprepare(ps_bridge->panel)) {
> > +		DRM_ERROR("failed to unprepare panel\n");
> > +		return;

Drop the return.

> > +	}
> > +}
> > +
> > +static int ps8640_get_modes(struct drm_connector *connector)
> > +{
> > +	struct ps8640 *ps_bridge = connector_to_ps8640(connector);
> > +	struct i2c_client *client = ps_bridge->client;
> > +
> > +	ps8640_prepare(ps_bridge);
> > +	ps8640_regw(client, ps_bridge->base_reg + 2, 0xea, 0xd0);
> > +
> > +	return drm_panel_get_modes(ps_bridge->panel);
> > +}
> > +
> > +static struct drm_encoder *ps8640_best_encoder(struct drm_connector *connector)
> > +{
> > +	struct ps8640 *ps_bridge;
> > +
> > +	ps_bridge = connector_to_ps8640(connector);
> > +	return ps_bridge->bridge.encoder;
> > +}
> > +
> > +static const struct drm_connector_helper_funcs
> > +	ps8640_connector_helper_funcs = {
> > +	.get_modes = ps8640_get_modes,
> > +	.best_encoder = ps8640_best_encoder,
> > +};
> > +
> > +static enum drm_connector_status ps8640_detect(struct drm_connector *connector,
> > +					       bool force)
> > +{
> > +	return connector_status_connected;
> > +}
> > +
> > +static void ps8640_connector_destroy(struct drm_connector *connector)
> > +{
> > +	drm_connector_unregister(connector);
> > +	drm_connector_cleanup(connector);
> > +}
> > +
> > +static const struct drm_connector_funcs ps8640_connector_funcs = {
> > +	.dpms = drm_atomic_helper_connector_dpms,
> > +	.fill_modes = drm_helper_probe_single_connector_modes,
> > +	.detect = ps8640_detect,
> > +	.destroy = ps8640_connector_destroy,
> > +	.reset = drm_atomic_helper_connector_reset,
> > +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> > +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> > +};
> > +
> > +int ps8640_bridge_attach(struct drm_bridge *bridge)
> > +{
> > +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> > +	int ret;
> > +
> > +	if (!bridge->encoder) {
> > +		DRM_ERROR("Parent encoder object not found");
> > +		return -ENODEV;
> > +	}
> > +
> > +	ret = drm_connector_init(bridge->dev, &ps_bridge->connector,
> > +				 &ps8640_connector_funcs,
> > +				 DRM_MODE_CONNECTOR_eDP);
> > +
> > +	if (ret) {
> > +		DRM_ERROR("Failed to initialize connector with drm\n");
> > +		return ret;
> > +	}
> > +
> > +	drm_connector_helper_add(&ps_bridge->connector,
> > +				 &ps8640_connector_helper_funcs);
> > +	drm_connector_register(&ps_bridge->connector);
> > +
> > +	ps_bridge->connector.dpms = DRM_MODE_DPMS_ON;
> > +	drm_mode_connector_attach_encoder(&ps_bridge->connector,
> > +					  bridge->encoder);
> > +
> > +	if (ps_bridge->panel)
> > +		drm_panel_attach(ps_bridge->panel, &ps_bridge->connector);
> > +
> > +	return ret;
> > +}
> > +
> > +static bool ps8640_bridge_mode_fixup(struct drm_bridge *bridge,
> > +				     const struct drm_display_mode *mode,
> > +				     struct drm_display_mode *adjusted_mode)
> > +{
> > +	return true;
> > +}
> > +
> > +static const struct drm_bridge_funcs ps8640_bridge_funcs = {
> > +	.attach = ps8640_bridge_attach,
> > +	.mode_fixup = ps8640_bridge_mode_fixup,
> > +	.disable = ps8640_disable,
> > +	.post_disable = ps8640_post_disable,
> > +	.pre_enable = ps8640_pre_enable,
> > +	.enable = ps8640_enable,
> > +};
> > +
> > +static int ps8640_probe(struct i2c_client *client,
> > +			const struct i2c_device_id *id)
> > +{
> > +	struct device *dev = &client->dev;
> > +	struct ps8640 *ps_bridge;
> > +	struct device_node *np = dev->of_node;
> > +	struct device_node *in_ep, *out_ep;
> > +	struct device_node *panel_node = NULL;
> > +	int ret;
> > +	u32 temp_reg;
> > +
> > +	ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL);
> > +	if (!ps_bridge)
> > +		return -ENOMEM;
> > +
> > +	in_ep = of_graph_get_next_endpoint(np, NULL);
> > +	if (in_ep) {
> > +		out_ep = of_graph_get_next_endpoint(np, in_ep);

Better use:
	port = of_graph_get_port_by_id(np, 1);
	out_ep = of_get_child_by_name(port, "endpoint");

> > +		of_node_put(in_ep);
> > +		if (out_ep) {
> > +			panel_node = of_graph_get_remote_port_parent(out_ep);
> > +			of_node_put(out_ep);
> > +		}
> > +	}
> > +	if (panel_node) {
> > +		ps_bridge->panel = of_drm_find_panel(panel_node);
> > +		of_node_put(panel_node);
> > +		if (!ps_bridge->panel)
> > +			return -EPROBE_DEFER;
> > +	}
> > +
> > +	ps_bridge->client = client;
> > +
> > +	ps_bridge->pwr_3v3_supply = devm_regulator_get(dev, "vdd33-supply");
> > +	if (IS_ERR(ps_bridge->pwr_3v3_supply)) {
> > +		dev_err(dev, "cannot get ps_bridge->pwr_3v3_supply\n");

Instead of printing the variable name, just say "vdd3-supply". Also, it
is good practice to print the actual error value.

> > +		return PTR_ERR(ps_bridge->pwr_3v3_supply);
> > +	}
> > +
> > +	ps_bridge->pwr_1v2_supply = devm_regulator_get(dev, "vdd12-supply");
> > +	if (IS_ERR(ps_bridge->pwr_1v2_supply)) {
> > +		dev_err(dev, "cannot get ps_bridge->pwr_1v2_supply\n");

Same as above.

> > +		return PTR_ERR(ps_bridge->pwr_1v2_supply);
> > +	}
> > +
> > +	ps_bridge->gpio_mode_sel_n = devm_gpiod_get(&client->dev, "mode-sel",
> > +						    GPIOD_OUT_HIGH);
> > +	if (IS_ERR(ps_bridge->gpio_mode_sel_n)) {
> > +		ret = PTR_ERR(ps_bridge->gpio_mode_sel_n);
> > +		DRM_ERROR("cannot get gpio_mode_sel_n %d\n", ret);

It would be better to use dev_err instead of DRM_ERROR. The same applies
to the DRM_ERRORs below.

> > +		return ret;
> > +	}
> > +
> > +	ret = gpiod_direction_output(ps_bridge->gpio_mode_sel_n, 1);
> > +	if (ret) {
> > +		DRM_ERROR("cannot configure gpio_mode_sel_n\n");
> > +		return ret;
> > +	}
> > +
> > +	ps_bridge->gpio_slp_n = devm_gpiod_get(&client->dev, "sleep-gpios",
> > +					       GPIOD_OUT_HIGH);
> > +	if (IS_ERR(ps_bridge->gpio_slp_n)) {
> > +		ret = PTR_ERR(ps_bridge->gpio_slp_n);
> > +		DRM_ERROR("cannot get gpio_slp_n %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = gpiod_direction_output(ps_bridge->gpio_slp_n, 1);
> > +	if (ret) {
> > +		DRM_ERROR("cannot configure gpio_slp_n\n");
> > +		return ret;
> > +	}
> > +
> > +	ps_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset",
> > +					       GPIOD_OUT_HIGH);
> > +	if (IS_ERR(ps_bridge->gpio_rst_n)) {
> > +		ret = PTR_ERR(ps_bridge->gpio_rst_n);
> > +		DRM_ERROR("cannot get gpio_rst_n %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = gpiod_direction_output(ps_bridge->gpio_rst_n, 1);
> > +	if (ret) {
> > +		DRM_ERROR("cannot configure gpio_rst_n\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = of_property_read_u32(dev->of_node, "reg", &temp_reg);
> > +	if (ret) {
> > +		DRM_ERROR("Can't read base_reg value\n");
> > +		return ret;
> > +	}
> > +	ps_bridge->base_reg = temp_reg;

No need for this, the i2c address is already stored in client->addr.

> > +	ps_bridge->bridge.funcs = &ps8640_bridge_funcs;
> > +	ps_bridge->bridge.of_node = dev->of_node;
> > +	ret = drm_bridge_add(&ps_bridge->bridge);
> > +	if (ret) {
> > +		DRM_ERROR("Failed to add bridge\n");
> > +		return ret;
> > +	}
> > +
> > +	i2c_set_clientdata(client, ps_bridge);
> > +
> > +	return 0;
> > +}
> > +
> > +static int ps8640_remove(struct i2c_client *client)
> > +{
> > +	struct ps8640 *ps_bridge = i2c_get_clientdata(client);
> > +
> > +	drm_bridge_remove(&ps_bridge->bridge);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct i2c_device_id ps8640_i2c_table[] = {
> > +	{"parade,ps8640", 0},
> > +	{},
> > +};
> > +MODULE_DEVICE_TABLE(i2c, ps8640_i2c_table);
> > +
> > +static const struct of_device_id ps8640_match[] = {
> > +	{ .compatible = "parade,ps8640" },
> > +	{},
> > +};
> > +MODULE_DEVICE_TABLE(of, ps8640_match);
> > +
> > +static struct i2c_driver ps8640_driver = {
> > +	.id_table = ps8640_i2c_table,
> > +	.probe = ps8640_probe,
> > +	.remove = ps8640_remove,
> > +	.driver = {
> > +		.name = "parade,ps8640",
> > +		.owner = THIS_MODULE,
> > +		.of_match_table = ps8640_match,
> > +	},
> > +};
> > +module_i2c_driver(ps8640_driver);
> > +
> > +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> > +MODULE_AUTHOR("CK Hu <ck.hu@mediatek.com>");
> > +MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver");
> > +MODULE_LICENSE("GPL v2");

best regards
Philipp


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

* Re: [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties
  2015-11-02  2:09 [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties Jitao Shi
  2015-11-02  2:09 ` [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge Jitao Shi
  2015-11-02  3:53 ` [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties jitao shi
@ 2015-11-02 11:35 ` Philipp Zabel
  2015-11-06  2:02 ` Rob Herring
  3 siblings, 0 replies; 11+ messages in thread
From: Philipp Zabel @ 2015-11-02 11:35 UTC (permalink / raw)
  To: Jitao Shi
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	David Airlie, Matthias Brugger, Thierry Reding, Ajay Kumar,
	Inki Dae, Rahul Sharma, Sean Paul, Vincent Palatin, Andy Yan,
	Russell King, devicetree, linux-kernel, dri-devel,
	linux-arm-kernel, linux-mediatek, srv_heupstream, Sascha Hauer,
	yingjoe.chen, eddie.huang, cawa.cheng, bibby.hsieh

Hi Jitao,

Am Montag, den 02.11.2015, 10:09 +0800 schrieb Jitao Shi:
> Add documentation for DT properties supported by
> ps8640 DSI-eDP converter.
> 
> Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
> ---
>  .../devicetree/bindings/display/bridge/ps8640.txt  |   43 ++++++++++++++++++++
>  1 file changed, 43 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/bridge/ps8640.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/bridge/ps8640.txt b/Documentation/devicetree/bindings/display/bridge/ps8640.txt
> new file mode 100644
> index 0000000..7edc547
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/bridge/ps8640.txt
> @@ -0,0 +1,43 @@
> +ps8640-bridge bindings
> +
> +Required properties:
> +	- compatible: "parade,ps8640"
> +	- reg: first page address of the bridge.
> +	- sleep-gpios: OF device-tree gpio specification for PD_ pin.
> +	- reset-gpios: OF device-tree gpio specification for reset pin.
> +	- mode-sel-gpios: OF device-tree gpio specification for mode-sel pin.
> +	- vdd12-supply: OF device-tree regulator specification for 1.2V power.
> +	- vdd33-supply: OF device-tree regulator specification for 3.3V power.
> +	- ports: The device node can contain video interface port nodes per
> +		 the video-interfaces bind[1]. For port@0,set the reg = <0> as
> +		 ps8640 dsi in and port@1,set the reg = <1> as ps8640 eDP out.
> +
> +[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
> +
> +Example:
> +	edp-bridge@18 {
> +		compatible = "parade,ps8640";
> +		reg = <0x18>;
> +		sleep-gpios = <&pio 116 GPIO_ACTIVE_HIGH>;
> +		reset-gpios = <&pio 115 GPIO_ACTIVE_HIGH>;
> +		mode-sel-gpios = <&pio 92 GPIO_ACTIVE_HIGH>;
> +		ps8640-1v2-supply = <&ps8640_fixed_1v2>;
> +		ps8640-3v3-supply = <&mt6397_vgp2_reg>;

These two should be updated to the new regulator property names.

regards
Philipp


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

* Re: [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties
  2015-11-02 11:09   ` Philipp Zabel
@ 2015-11-05  1:15     ` jitao shi
  0 siblings, 0 replies; 11+ messages in thread
From: jitao shi @ 2015-11-05  1:15 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Rob Herring, Ian Campbell, Kumar Gala, David Airlie,
	Matthias Brugger, Thierry Reding, Ajay Kumar, Inki Dae,
	Rahul Sharma, Sean Paul, Vincent Palatin, Andy Yan, Russell King,
	devicetree, linux-kernel, dri-devel, linux-arm-kernel,
	linux-mediatek, srv_heupstream, Sascha Hauer, yingjoe.chen,
	eddie.huang, cawa.cheng, bibby.hsieh, Pawel Moll, Mark Rutland,
	ck.hu

On Mon, 2015-11-02 at 12:09 +0100, Philipp Zabel wrote:
> Hi Jitao,
> 
> Am Montag, den 02.11.2015, 11:53 +0800 schrieb jitao shi:
> [...]
> > > +Example:
> > > +	edp-bridge@18 {
> > > +		compatible = "parade,ps8640";
> > > +		reg = <0x18>;
> > > +		sleep-gpios = <&pio 116 GPIO_ACTIVE_HIGH>;
> > > +		reset-gpios = <&pio 115 GPIO_ACTIVE_HIGH>;
> > > +		mode-sel-gpios = <&pio 92 GPIO_ACTIVE_HIGH>;
> > > +		ps8640-1v2-supply = <&ps8640_fixed_1v2>;
> 
> This should be vdd12-supply now.

Thanks a lot.  I'll fix it on V3.

> 
> > > +		ps8640-3v3-supply = <&mt6397_vgp2_reg>;
> 
> Should be vdd33-supply now.

I'll fix it on V3.

> 
> regards
> Philipp
> 



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

* Re: [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge
  2015-11-02 11:34     ` Philipp Zabel
@ 2015-11-05  1:22       ` jitao shi
  0 siblings, 0 replies; 11+ messages in thread
From: jitao shi @ 2015-11-05  1:22 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Rob Herring, Ian Campbell, Kumar Gala, David Airlie,
	Matthias Brugger, Thierry Reding, Ajay Kumar, Inki Dae,
	Rahul Sharma, Sean Paul, Vincent Palatin, Andy Yan, Russell King,
	devicetree, linux-kernel, dri-devel, linux-arm-kernel,
	linux-mediatek, srv_heupstream, Sascha Hauer, yingjoe.chen,
	eddie.huang, cawa.cheng, bibby.hsieh, Pawel Moll, Mark Rutland,
	ck.hu

Hi Philipp

I'll fixed those comments on V3.

Thank you very much.

Best Regards.

On Mon, 2015-11-02 at 12:34 +0100, Philipp Zabel wrote:
> Hi Jitao,
> 
> a few comments below.
> 
> Am Montag, den 02.11.2015, 11:54 +0800 schrieb jitao shi:
> [...]
> > > +static int ps8640_check_valid_id(struct ps8640 *ps_bridge)
> 
> This could be bool and return true/false.
> 
> > > +{
> > > +	struct i2c_client *client = ps_bridge->client;
> > > +	u8 rev_id_low, rev_id_high, chip_id_low, chip_id_high;
> > > +	int retry_cnt = 0;
> > > +
> > > +	do {
> > > +		ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_CHIP_H,
> > > +			    &chip_id_high);
> > > +		if (chip_id_high != 0x30)
> > > +			DRM_INFO("chip_id_high = 0x%x\n", chip_id_high);
> > > +	} while ((retry_cnt++ < 2) && (chip_id_high != 0x30));
> > > +
> > > +	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_REV_L, &rev_id_low);
> > > +	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_REV_H, &rev_id_high);
> > > +	ps8640_regr(client, ps_bridge->base_reg + 4, PAGE4_CHIP_L,
> > > +		    &chip_id_low);
> > > +
> > > +	if ((rev_id_low == 0x00) && (rev_id_high == 0x0a) &&
> > > +	    (chip_id_low == 0x00) && (chip_id_high == 0x30))
> > > +		return 1;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static void ps8640_show_mcu_fw_version(struct ps8640 *ps_bridge)
> > > +{
> > > +	struct i2c_client *client = ps_bridge->client;
> > > +	u8 major_ver, minor_ver;
> > > +
> > > +	ps8640_regr(client, ps_bridge->base_reg + 5, 0x4, &major_ver);
> > > +	ps8640_regr(client, ps_bridge->base_reg + 5, 0x5, &minor_ver);
> > > +
> > > +	DRM_INFO_ONCE("ps8640 rom fw version %d.%d\n", major_ver, minor_ver);
> > > +}
> > > +
> > > +static int ps8640_bdg_enable(struct ps8640 *ps_bridge)
> > > +{
> > > +	struct i2c_client *client = ps_bridge->client;
> > > +
> > > +	if (ps8640_check_valid_id(ps_bridge)) {
> > > +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xfe, 0x13);
> > > +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xff, 0x18);
> > > +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xfe, 0x13);
> > > +		ps8640_regw(client, ps_bridge->base_reg + 3, 0xff, 0x1c);
> 
> Can you introduce #defines with descriptive names for those magic
> constants and register offsets, and maybe also i2c address offsets?
> 
> > > +
> > > +		return 0;
> > > +	}
> > > +
> > > +	return -1;
> 
> Never return -1 to signal a problem.
> If there was an error, use a proper error code.
> 
> > > +}
> > > +
> > > +static void ps8640_prepare(struct ps8640 *ps_bridge)
> > > +{
> > > +	struct i2c_client *client = ps_bridge->client;
> > > +	int err, retry_cnt = 0;
> > > +	u8 set_vdo_done;
> > > +
> > > +	if (ps_bridge->enabled)
> > > +		return;
> > > +
> > > +	if (drm_panel_prepare(ps_bridge->panel)) {
> > > +		DRM_ERROR("failed to prepare panel\n");
> > > +		return;
> > > +	}
> > > +
> > > +	err = regulator_enable(ps_bridge->pwr_1v2_supply);
> > > +	if (err < 0) {
> > > +		DRM_ERROR("failed to enable pwr_1v2_supply: %d\n", err);
> 
> Missing panel unprepare.
> 
> > > +		return;
> > > +	}
> > > +
> > > +	err = regulator_enable(ps_bridge->pwr_3v3_supply);
> > > +	if (err < 0) {
> > > +		DRM_ERROR("failed to enable pwr_3v3_supply: %d\n", err);
> 
> Missing panel unprepare and vdd12 regulator disable.
> 
> > > +		return;
> > > +	}
> > > +
> > > +	gpiod_set_value(ps_bridge->gpio_slp_n, 1);
> > > +	gpiod_set_value(ps_bridge->gpio_rst_n, 0);
> > > +	usleep_range(500, 700);
> > > +	gpiod_set_value(ps_bridge->gpio_rst_n, 1);
> > > +
> > > +	do {
> > > +		msleep(50);
> > > +		ps8640_regr(client, ps_bridge->base_reg + 2, PAGE2_GPIO_H,
> > > +			    &set_vdo_done);
> > > +	} while ((retry_cnt++ < 70) && ((set_vdo_done & PS_GPIO9) != PS_GPIO9));
> > > +
> > > +	ps8640_show_mcu_fw_version(ps_bridge);
> > > +	ps_bridge->enabled = true;
> > > +}
> > > +
> > > +static void ps8640_pre_enable(struct drm_bridge *bridge)
> > > +{
> > > +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> > > +
> > > +	ps8640_prepare(ps_bridge);
> > > +}
> > > +
> > > +static void ps8640_enable(struct drm_bridge *bridge)
> > > +{
> > > +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> > > +
> > > +	ps8640_bdg_enable(ps_bridge);
> > > +
> > > +	if (drm_panel_enable(ps_bridge->panel)) {
> > > +		DRM_ERROR("failed to enable panel\n");
> > > +		return;
> 
> The return is superfluous.
> 
> > > +	}
> > > +}
> > > +
> > > +static void ps8640_disable(struct drm_bridge *bridge)
> > > +{
> > > +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> > > +
> > > +	if (!ps_bridge->enabled)
> > > +		return;
> > > +
> > > +	ps_bridge->enabled = false;
> > > +
> > > +	if (drm_panel_disable(ps_bridge->panel)) {
> > > +		DRM_ERROR("failed to disable panel\n");
> > > +		return;
> 
> Shouldn't we still disable the bridge, even if panel disable fails?
> 
> > > +	}
> > > +
> > > +	regulator_disable(ps_bridge->pwr_1v2_supply);
> > > +	regulator_disable(ps_bridge->pwr_3v3_supply);
> > > +	gpiod_set_value(ps_bridge->gpio_rst_n, 0);
> > > +	gpiod_set_value(ps_bridge->gpio_slp_n, 0);
> > > +}
> > > +
> > > +static void ps8640_post_disable(struct drm_bridge *bridge)
> > > +{
> > > +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> > > +
> > > +	if (drm_panel_unprepare(ps_bridge->panel)) {
> > > +		DRM_ERROR("failed to unprepare panel\n");
> > > +		return;
> 
> Drop the return.
> 
> > > +	}
> > > +}
> > > +
> > > +static int ps8640_get_modes(struct drm_connector *connector)
> > > +{
> > > +	struct ps8640 *ps_bridge = connector_to_ps8640(connector);
> > > +	struct i2c_client *client = ps_bridge->client;
> > > +
> > > +	ps8640_prepare(ps_bridge);
> > > +	ps8640_regw(client, ps_bridge->base_reg + 2, 0xea, 0xd0);
> > > +
> > > +	return drm_panel_get_modes(ps_bridge->panel);
> > > +}
> > > +
> > > +static struct drm_encoder *ps8640_best_encoder(struct drm_connector *connector)
> > > +{
> > > +	struct ps8640 *ps_bridge;
> > > +
> > > +	ps_bridge = connector_to_ps8640(connector);
> > > +	return ps_bridge->bridge.encoder;
> > > +}
> > > +
> > > +static const struct drm_connector_helper_funcs
> > > +	ps8640_connector_helper_funcs = {
> > > +	.get_modes = ps8640_get_modes,
> > > +	.best_encoder = ps8640_best_encoder,
> > > +};
> > > +
> > > +static enum drm_connector_status ps8640_detect(struct drm_connector *connector,
> > > +					       bool force)
> > > +{
> > > +	return connector_status_connected;
> > > +}
> > > +
> > > +static void ps8640_connector_destroy(struct drm_connector *connector)
> > > +{
> > > +	drm_connector_unregister(connector);
> > > +	drm_connector_cleanup(connector);
> > > +}
> > > +
> > > +static const struct drm_connector_funcs ps8640_connector_funcs = {
> > > +	.dpms = drm_atomic_helper_connector_dpms,
> > > +	.fill_modes = drm_helper_probe_single_connector_modes,
> > > +	.detect = ps8640_detect,
> > > +	.destroy = ps8640_connector_destroy,
> > > +	.reset = drm_atomic_helper_connector_reset,
> > > +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> > > +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> > > +};
> > > +
> > > +int ps8640_bridge_attach(struct drm_bridge *bridge)
> > > +{
> > > +	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> > > +	int ret;
> > > +
> > > +	if (!bridge->encoder) {
> > > +		DRM_ERROR("Parent encoder object not found");
> > > +		return -ENODEV;
> > > +	}
> > > +
> > > +	ret = drm_connector_init(bridge->dev, &ps_bridge->connector,
> > > +				 &ps8640_connector_funcs,
> > > +				 DRM_MODE_CONNECTOR_eDP);
> > > +
> > > +	if (ret) {
> > > +		DRM_ERROR("Failed to initialize connector with drm\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	drm_connector_helper_add(&ps_bridge->connector,
> > > +				 &ps8640_connector_helper_funcs);
> > > +	drm_connector_register(&ps_bridge->connector);
> > > +
> > > +	ps_bridge->connector.dpms = DRM_MODE_DPMS_ON;
> > > +	drm_mode_connector_attach_encoder(&ps_bridge->connector,
> > > +					  bridge->encoder);
> > > +
> > > +	if (ps_bridge->panel)
> > > +		drm_panel_attach(ps_bridge->panel, &ps_bridge->connector);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static bool ps8640_bridge_mode_fixup(struct drm_bridge *bridge,
> > > +				     const struct drm_display_mode *mode,
> > > +				     struct drm_display_mode *adjusted_mode)
> > > +{
> > > +	return true;
> > > +}
> > > +
> > > +static const struct drm_bridge_funcs ps8640_bridge_funcs = {
> > > +	.attach = ps8640_bridge_attach,
> > > +	.mode_fixup = ps8640_bridge_mode_fixup,
> > > +	.disable = ps8640_disable,
> > > +	.post_disable = ps8640_post_disable,
> > > +	.pre_enable = ps8640_pre_enable,
> > > +	.enable = ps8640_enable,
> > > +};
> > > +
> > > +static int ps8640_probe(struct i2c_client *client,
> > > +			const struct i2c_device_id *id)
> > > +{
> > > +	struct device *dev = &client->dev;
> > > +	struct ps8640 *ps_bridge;
> > > +	struct device_node *np = dev->of_node;
> > > +	struct device_node *in_ep, *out_ep;
> > > +	struct device_node *panel_node = NULL;
> > > +	int ret;
> > > +	u32 temp_reg;
> > > +
> > > +	ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL);
> > > +	if (!ps_bridge)
> > > +		return -ENOMEM;
> > > +
> > > +	in_ep = of_graph_get_next_endpoint(np, NULL);
> > > +	if (in_ep) {
> > > +		out_ep = of_graph_get_next_endpoint(np, in_ep);
> 
> Better use:
> 	port = of_graph_get_port_by_id(np, 1);
> 	out_ep = of_get_child_by_name(port, "endpoint");
> 
> > > +		of_node_put(in_ep);
> > > +		if (out_ep) {
> > > +			panel_node = of_graph_get_remote_port_parent(out_ep);
> > > +			of_node_put(out_ep);
> > > +		}
> > > +	}
> > > +	if (panel_node) {
> > > +		ps_bridge->panel = of_drm_find_panel(panel_node);
> > > +		of_node_put(panel_node);
> > > +		if (!ps_bridge->panel)
> > > +			return -EPROBE_DEFER;
> > > +	}
> > > +
> > > +	ps_bridge->client = client;
> > > +
> > > +	ps_bridge->pwr_3v3_supply = devm_regulator_get(dev, "vdd33-supply");
> > > +	if (IS_ERR(ps_bridge->pwr_3v3_supply)) {
> > > +		dev_err(dev, "cannot get ps_bridge->pwr_3v3_supply\n");
> 
> Instead of printing the variable name, just say "vdd3-supply". Also, it
> is good practice to print the actual error value.
> 
> > > +		return PTR_ERR(ps_bridge->pwr_3v3_supply);
> > > +	}
> > > +
> > > +	ps_bridge->pwr_1v2_supply = devm_regulator_get(dev, "vdd12-supply");
> > > +	if (IS_ERR(ps_bridge->pwr_1v2_supply)) {
> > > +		dev_err(dev, "cannot get ps_bridge->pwr_1v2_supply\n");
> 
> Same as above.
> 
> > > +		return PTR_ERR(ps_bridge->pwr_1v2_supply);
> > > +	}
> > > +
> > > +	ps_bridge->gpio_mode_sel_n = devm_gpiod_get(&client->dev, "mode-sel",
> > > +						    GPIOD_OUT_HIGH);
> > > +	if (IS_ERR(ps_bridge->gpio_mode_sel_n)) {
> > > +		ret = PTR_ERR(ps_bridge->gpio_mode_sel_n);
> > > +		DRM_ERROR("cannot get gpio_mode_sel_n %d\n", ret);
> 
> It would be better to use dev_err instead of DRM_ERROR. The same applies
> to the DRM_ERRORs below.
> 
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = gpiod_direction_output(ps_bridge->gpio_mode_sel_n, 1);
> > > +	if (ret) {
> > > +		DRM_ERROR("cannot configure gpio_mode_sel_n\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	ps_bridge->gpio_slp_n = devm_gpiod_get(&client->dev, "sleep-gpios",
> > > +					       GPIOD_OUT_HIGH);
> > > +	if (IS_ERR(ps_bridge->gpio_slp_n)) {
> > > +		ret = PTR_ERR(ps_bridge->gpio_slp_n);
> > > +		DRM_ERROR("cannot get gpio_slp_n %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = gpiod_direction_output(ps_bridge->gpio_slp_n, 1);
> > > +	if (ret) {
> > > +		DRM_ERROR("cannot configure gpio_slp_n\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	ps_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset",
> > > +					       GPIOD_OUT_HIGH);
> > > +	if (IS_ERR(ps_bridge->gpio_rst_n)) {
> > > +		ret = PTR_ERR(ps_bridge->gpio_rst_n);
> > > +		DRM_ERROR("cannot get gpio_rst_n %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = gpiod_direction_output(ps_bridge->gpio_rst_n, 1);
> > > +	if (ret) {
> > > +		DRM_ERROR("cannot configure gpio_rst_n\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = of_property_read_u32(dev->of_node, "reg", &temp_reg);
> > > +	if (ret) {
> > > +		DRM_ERROR("Can't read base_reg value\n");
> > > +		return ret;
> > > +	}
> > > +	ps_bridge->base_reg = temp_reg;
> 
> No need for this, the i2c address is already stored in client->addr.
> 
> > > +	ps_bridge->bridge.funcs = &ps8640_bridge_funcs;
> > > +	ps_bridge->bridge.of_node = dev->of_node;
> > > +	ret = drm_bridge_add(&ps_bridge->bridge);
> > > +	if (ret) {
> > > +		DRM_ERROR("Failed to add bridge\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	i2c_set_clientdata(client, ps_bridge);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int ps8640_remove(struct i2c_client *client)
> > > +{
> > > +	struct ps8640 *ps_bridge = i2c_get_clientdata(client);
> > > +
> > > +	drm_bridge_remove(&ps_bridge->bridge);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct i2c_device_id ps8640_i2c_table[] = {
> > > +	{"parade,ps8640", 0},
> > > +	{},
> > > +};
> > > +MODULE_DEVICE_TABLE(i2c, ps8640_i2c_table);
> > > +
> > > +static const struct of_device_id ps8640_match[] = {
> > > +	{ .compatible = "parade,ps8640" },
> > > +	{},
> > > +};
> > > +MODULE_DEVICE_TABLE(of, ps8640_match);
> > > +
> > > +static struct i2c_driver ps8640_driver = {
> > > +	.id_table = ps8640_i2c_table,
> > > +	.probe = ps8640_probe,
> > > +	.remove = ps8640_remove,
> > > +	.driver = {
> > > +		.name = "parade,ps8640",
> > > +		.owner = THIS_MODULE,
> > > +		.of_match_table = ps8640_match,
> > > +	},
> > > +};
> > > +module_i2c_driver(ps8640_driver);
> > > +
> > > +MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
> > > +MODULE_AUTHOR("CK Hu <ck.hu@mediatek.com>");
> > > +MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver");
> > > +MODULE_LICENSE("GPL v2");
> 
> best regards
> Philipp
> 



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

* Re: [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge
  2015-11-02  2:09 ` [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge Jitao Shi
  2015-11-02  3:54   ` jitao shi
@ 2015-11-05  9:09   ` Philipp Zabel
  1 sibling, 0 replies; 11+ messages in thread
From: Philipp Zabel @ 2015-11-05  9:09 UTC (permalink / raw)
  To: Jitao Shi
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	David Airlie, Matthias Brugger, devicetree, eddie.huang,
	Sascha Hauer, srv_heupstream, bibby.hsieh, linux-mediatek,
	Russell King, dri-devel, linux-kernel, Inki Dae, yingjoe.chen,
	cawa.cheng, Sean Paul, Andy Yan, Vincent Palatin, Thierry Reding,
	Ajay Kumar, linux-arm-kernel, Rahul Sharma

Hi Jitao,

some things I missed before.

Am Montag, den 02.11.2015, 10:09 +0800 schrieb Jitao Shi:
[...]
> +static int ps8640_regr(struct i2c_client *client, u16 i2c_addr,
> +		       u8 reg, u8 *value)
> +{
> +	int ret;
> +
> +	client->addr = i2c_addr;

I think i2c_new_dummy should be used to create additional clients for
the secondary addresses, instead of changing the address of the client
with every transfer.

> +	ret = i2c_master_send(client, &reg, 1);
> +	if (ret <= 0) {
> +		DRM_ERROR("Failed to send i2c command, ret=%d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = i2c_master_recv(client, value, 1);
> +	if (ret <= 0) {
> +		DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
[...]
> +static int ps8640_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct device *dev = &client->dev;
> +	struct ps8640 *ps_bridge;
> +	struct device_node *np = dev->of_node;
> +	struct device_node *in_ep, *out_ep;
> +	struct device_node *panel_node = NULL;
> +	int ret;
> +	u32 temp_reg;
> +
> +	ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL);
> +	if (!ps_bridge)
> +		return -ENOMEM;
> +
> +	in_ep = of_graph_get_next_endpoint(np, NULL);
> +	if (in_ep) {
> +		out_ep = of_graph_get_next_endpoint(np, in_ep);
> +		of_node_put(in_ep);
> +		if (out_ep) {
> +			panel_node = of_graph_get_remote_port_parent(out_ep);
> +			of_node_put(out_ep);
> +		}
> +	}
> +	if (panel_node) {
> +		ps_bridge->panel = of_drm_find_panel(panel_node);
> +		of_node_put(panel_node);
> +		if (!ps_bridge->panel)
> +			return -EPROBE_DEFER;
> +	}
> +
> +	ps_bridge->client = client;
> +
> +	ps_bridge->pwr_3v3_supply = devm_regulator_get(dev, "vdd33-supply");

Should be "vdd3", regulator_get will add the "-supply" suffix.

> +	if (IS_ERR(ps_bridge->pwr_3v3_supply)) {
> +		dev_err(dev, "cannot get ps_bridge->pwr_3v3_supply\n");
> +		return PTR_ERR(ps_bridge->pwr_3v3_supply);
> +	}
> +
> +	ps_bridge->pwr_1v2_supply = devm_regulator_get(dev, "vdd12-supply");

Same here, "vdd12".

> +	if (IS_ERR(ps_bridge->pwr_1v2_supply)) {
> +		dev_err(dev, "cannot get ps_bridge->pwr_1v2_supply\n");
> +		return PTR_ERR(ps_bridge->pwr_1v2_supply);
> +	}
> +
> +	ps_bridge->gpio_mode_sel_n = devm_gpiod_get(&client->dev, "mode-sel",
> +						    GPIOD_OUT_HIGH);
> +	if (IS_ERR(ps_bridge->gpio_mode_sel_n)) {
> +		ret = PTR_ERR(ps_bridge->gpio_mode_sel_n);
> +		DRM_ERROR("cannot get gpio_mode_sel_n %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = gpiod_direction_output(ps_bridge->gpio_mode_sel_n, 1);
> +	if (ret) {
> +		DRM_ERROR("cannot configure gpio_mode_sel_n\n");
> +		return ret;
> +	}
> +
> +	ps_bridge->gpio_slp_n = devm_gpiod_get(&client->dev, "sleep-gpios",
> +					       GPIOD_OUT_HIGH);

Should be "sleep", gpiod_get will add the "-gpios" suffix.

> +	if (IS_ERR(ps_bridge->gpio_slp_n)) {
> +		ret = PTR_ERR(ps_bridge->gpio_slp_n);
> +		DRM_ERROR("cannot get gpio_slp_n %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = gpiod_direction_output(ps_bridge->gpio_slp_n, 1);
> +	if (ret) {
> +		DRM_ERROR("cannot configure gpio_slp_n\n");
> +		return ret;
> +	}

This can be removed, the "devm_gpiod_get(..., GPIOD_OUT_HIGH);" already
does the same.

> +
> +	ps_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset",
> +					       GPIOD_OUT_HIGH);
> +	if (IS_ERR(ps_bridge->gpio_rst_n)) {
> +		ret = PTR_ERR(ps_bridge->gpio_rst_n);
> +		DRM_ERROR("cannot get gpio_rst_n %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = gpiod_direction_output(ps_bridge->gpio_rst_n, 1);
> +	if (ret) {
> +		DRM_ERROR("cannot configure gpio_rst_n\n");
> +		return ret;
> +	}

Same here, the gpiod_direction_output can be removed.

best regards
Philipp


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

* Re: [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties
  2015-11-02  2:09 [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties Jitao Shi
                   ` (2 preceding siblings ...)
  2015-11-02 11:35 ` Philipp Zabel
@ 2015-11-06  2:02 ` Rob Herring
  3 siblings, 0 replies; 11+ messages in thread
From: Rob Herring @ 2015-11-06  2:02 UTC (permalink / raw)
  To: Jitao Shi
  Cc: Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, David Airlie,
	Matthias Brugger, Thierry Reding, Ajay Kumar, Inki Dae,
	Rahul Sharma, Sean Paul, Vincent Palatin, Andy Yan,
	Philipp Zabel, Russell King, devicetree, linux-kernel, dri-devel,
	linux-arm-kernel, linux-mediatek, srv_heupstream, Sascha Hauer,
	yingjoe.chen, eddie.huang, cawa.cheng, bibby.hsieh

On Mon, Nov 02, 2015 at 10:09:25AM +0800, Jitao Shi wrote:
> Add documentation for DT properties supported by
> ps8640 DSI-eDP converter.
> 
> Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  .../devicetree/bindings/display/bridge/ps8640.txt  |   43 ++++++++++++++++++++
>  1 file changed, 43 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/bridge/ps8640.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/bridge/ps8640.txt b/Documentation/devicetree/bindings/display/bridge/ps8640.txt
> new file mode 100644
> index 0000000..7edc547
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/bridge/ps8640.txt
> @@ -0,0 +1,43 @@
> +ps8640-bridge bindings
> +
> +Required properties:
> +	- compatible: "parade,ps8640"
> +	- reg: first page address of the bridge.
> +	- sleep-gpios: OF device-tree gpio specification for PD_ pin.
> +	- reset-gpios: OF device-tree gpio specification for reset pin.
> +	- mode-sel-gpios: OF device-tree gpio specification for mode-sel pin.
> +	- vdd12-supply: OF device-tree regulator specification for 1.2V power.
> +	- vdd33-supply: OF device-tree regulator specification for 3.3V power.
> +	- ports: The device node can contain video interface port nodes per
> +		 the video-interfaces bind[1]. For port@0,set the reg = <0> as
> +		 ps8640 dsi in and port@1,set the reg = <1> as ps8640 eDP out.
> +
> +[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
> +
> +Example:
> +	edp-bridge@18 {
> +		compatible = "parade,ps8640";
> +		reg = <0x18>;
> +		sleep-gpios = <&pio 116 GPIO_ACTIVE_HIGH>;
> +		reset-gpios = <&pio 115 GPIO_ACTIVE_HIGH>;
> +		mode-sel-gpios = <&pio 92 GPIO_ACTIVE_HIGH>;
> +		ps8640-1v2-supply = <&ps8640_fixed_1v2>;
> +		ps8640-3v3-supply = <&mt6397_vgp2_reg>;
> +
> +		ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			port@0 {
> +				reg = <0>;
> +				ps8640_in: endpoint {
> +					remote-endpoint = <&dsi0_out>;
> +				};
> +			};
> +			port@1 {
> +				reg = <1>;
> +				ps8640_out: endpoint {
> +					remote-endpoint = <&panel_in>;
> +				};
> +			};
> +		};
> +	};
> -- 
> 1.7.9.5
> 

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

end of thread, other threads:[~2015-11-06  2:03 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-02  2:09 [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties Jitao Shi
2015-11-02  2:09 ` [RFC v2 2/2] drm/bridge: Add I2C based driver for ps8640 bridge Jitao Shi
2015-11-02  3:54   ` jitao shi
2015-11-02 11:34     ` Philipp Zabel
2015-11-05  1:22       ` jitao shi
2015-11-05  9:09   ` Philipp Zabel
2015-11-02  3:53 ` [RFC v2 1/2] Documentation: bridge: Add documentation for ps8640 DT properties jitao shi
2015-11-02 11:09   ` Philipp Zabel
2015-11-05  1:15     ` jitao shi
2015-11-02 11:35 ` Philipp Zabel
2015-11-06  2:02 ` Rob Herring

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®