2023-02-18 11:17:26

by Pin-yen Lin

[permalink] [raw]
Subject: [PATCH v3 0/5] Add generic-display-mux driver and bindings

This series is developed for and tested on MT8173 board, and the layout is:

/-- anx7688
-- MT8173 HDMI bridge -- GPIO mux
\-- native HDMI

v2: https://lore.kernel.org/all/[email protected]/
v1: https://patchwork.kernel.org/project/dri-devel/cover/[email protected]/

Changes in v3:
- Removed ddc-i2c-bus in the gpio-mux bridge bindings
- Added .get_edid callback in generic-gpio-mux driver
- Removed .get_edid callback in mtk_hdmi.c
- Modified anx7688 driver and binding to add a .get_edid callback

Changes in v2:
- Referenced existing dt-binding schemas from graph.yaml
- Added ddc-i2c-bus into the bindings
- Dropped attach/mode_set/enable/disable callbacks
- Fixed style issues
- Removed the special case for the HDMI connector
- Made the driver only read the GPIO status in IRQ handler
- Rebased to drm-misc-next
- Update the license: "GPL v2" --> "GPL"

Nicolas Boichat (2):
dt-bindings: display: bridge: Add GPIO display mux binding
drm: bridge: Generic GPIO mux driver

Pin-yen Lin (3):
dt-bindings: display: bridge: Add ddc-i2c-bus for anx7688
drm/bridge: Add .get_edid callback for anx7688 driver
drm/mediatek: Remove .get_edid callback

.../bridge/google,cros-ec-anx7688.yaml | 5 +
.../bindings/display/bridge/gpio-mux.yaml | 90 +++++++
drivers/gpu/drm/bridge/Kconfig | 10 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/cros-ec-anx7688.c | 24 ++
drivers/gpu/drm/bridge/generic-gpio-mux.c | 222 ++++++++++++++++++
drivers/gpu/drm/mediatek/mtk_hdmi.c | 53 +----
7 files changed, 365 insertions(+), 40 deletions(-)
create mode 100644 Documentation/devicetree/bindings/display/bridge/gpio-mux.yaml
create mode 100644 drivers/gpu/drm/bridge/generic-gpio-mux.c

--
2.39.2.637.g21b0678d19-goog



2023-02-18 11:17:40

by Pin-yen Lin

[permalink] [raw]
Subject: [PATCH v3 1/5] dt-bindings: display: bridge: Add ddc-i2c-bus for anx7688

Introduce a optional "ddc-i2c-bus" property for anx7688 bridge. This
allows the bridge to register a .get_edid callback.

Signed-off-by: Pin-yen Lin <[email protected]>
---

Changes in v3:
- New in v3

.../bindings/display/bridge/google,cros-ec-anx7688.yaml | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml b/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml
index a44d025d33bd..9d5ce8172e88 100644
--- a/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml
@@ -25,6 +25,10 @@ properties:
maxItems: 1
description: I2C address of the device.

+ ddc-i2c-bus:
+ description: phandle link to the I2C controller used for DDC EDID probing
+ $ref: /schemas/types.yaml#/definitions/phandle
+
ports:
$ref: /schemas/graph.yaml#/properties/ports

@@ -59,6 +63,7 @@ examples:
anx7688: anx7688@2c {
compatible = "google,cros-ec-anx7688";
reg = <0x2c>;
+ ddc-i2c-bus = <&hdmiddc0>;

ports {
#address-cells = <1>;
--
2.39.2.637.g21b0678d19-goog


2023-02-18 11:17:51

by Pin-yen Lin

[permalink] [raw]
Subject: [PATCH v3 2/5] drm/bridge: Add .get_edid callback for anx7688 driver

Allow the driver to read EDID when ddc-i2c-bus phandle is present in
the device node.

Signed-off-by: Pin-yen Lin <[email protected]>
---

Changes in v3:
- New in v3

drivers/gpu/drm/bridge/cros-ec-anx7688.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/drivers/gpu/drm/bridge/cros-ec-anx7688.c b/drivers/gpu/drm/bridge/cros-ec-anx7688.c
index fa91bdeddef0..a16294c87940 100644
--- a/drivers/gpu/drm/bridge/cros-ec-anx7688.c
+++ b/drivers/gpu/drm/bridge/cros-ec-anx7688.c
@@ -6,6 +6,7 @@
*/

#include <drm/drm_bridge.h>
+#include <drm/drm_edid.h>
#include <drm/drm_print.h>
#include <linux/i2c.h>
#include <linux/module.h>
@@ -91,14 +92,24 @@ static bool cros_ec_anx7688_bridge_mode_fixup(struct drm_bridge *bridge,
return totalbw >= requiredbw;
}

+static struct edid *cros_ec_anx7688_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct cros_ec_anx7688 *anx7688 = bridge_to_cros_ec_anx7688(bridge);
+
+ return drm_get_edid(connector, anx7688->bridge.ddc);
+}
+
static const struct drm_bridge_funcs cros_ec_anx7688_bridge_funcs = {
.mode_fixup = cros_ec_anx7688_bridge_mode_fixup,
+ .get_edid = cros_ec_anx7688_get_edid,
};

static int cros_ec_anx7688_bridge_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct cros_ec_anx7688 *anx7688;
+ struct device_node *phandle;
u16 vendor, device, fw_version;
u8 buffer[4];
int ret;
@@ -153,6 +164,19 @@ static int cros_ec_anx7688_bridge_probe(struct i2c_client *client)
DRM_WARN("Old ANX7688 FW version (0x%04x), not filtering\n",
fw_version);

+
+ phandle = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
+ if (phandle) {
+ anx7688->bridge.ddc = of_get_i2c_adapter_by_node(phandle);
+ of_node_put(phandle);
+ if (!anx7688->bridge.ddc)
+ return -EPROBE_DEFER;
+ } else {
+ dev_dbg(dev, "No I2C bus specified, disabling EDID readout\n");
+ }
+ if (anx7688->bridge.ddc)
+ anx7688->bridge.ops |= DRM_BRIDGE_OP_EDID;
+
anx7688->bridge.funcs = &cros_ec_anx7688_bridge_funcs;
drm_bridge_add(&anx7688->bridge);

--
2.39.2.637.g21b0678d19-goog


2023-02-18 11:18:03

by Pin-yen Lin

[permalink] [raw]
Subject: [PATCH v3 3/5] drm/mediatek: Remove .get_edid callback

The original implementation peaking into the remote nodes to get the
ddc bus fwnode, which is not a good practice. Remove the callback from
this driver and rely on drm_connector helpers to read EDID.

Signed-off-by: Pin-yen Lin <[email protected]>
---

Changes in v3:
- New in v3

drivers/gpu/drm/mediatek/mtk_hdmi.c | 53 +++++++----------------------
1 file changed, 13 insertions(+), 40 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index 0a8e0a13f516..44952e539059 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -159,7 +159,6 @@ struct mtk_hdmi {
const struct mtk_hdmi_conf *conf;
struct phy *phy;
struct device *cec_dev;
- struct i2c_adapter *ddc_adpt;
struct clk *clk[MTK_HDMI_CLK_COUNT];
struct drm_display_mode mode;
bool dvi_mode;
@@ -1265,21 +1264,6 @@ static enum drm_connector_status mtk_hdmi_bridge_detect(struct drm_bridge *bridg
return mtk_hdmi_detect(hdmi);
}

-static struct edid *mtk_hdmi_bridge_get_edid(struct drm_bridge *bridge,
- struct drm_connector *connector)
-{
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
- struct edid *edid;
-
- if (!hdmi->ddc_adpt)
- return NULL;
- edid = drm_get_edid(connector, hdmi->ddc_adpt);
- if (!edid)
- return NULL;
- hdmi->dvi_mode = !drm_detect_monitor_audio(edid);
- return edid;
-}
-
static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
@@ -1390,10 +1374,19 @@ static void mtk_hdmi_bridge_atomic_enable(struct drm_bridge *bridge,
{
struct drm_atomic_state *state = old_state->base.state;
struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+ struct edid *edid;

/* Retrieve the connector through the atomic state. */
- hdmi->curr_conn = drm_atomic_get_new_connector_for_encoder(state,
- bridge->encoder);
+ if (!hdmi->curr_conn)
+ hdmi->curr_conn = drm_atomic_get_new_connector_for_encoder(
+ state, bridge->encoder);
+
+ if (hdmi->curr_conn->edid_blob_ptr) {
+ edid = (struct edid *)hdmi->curr_conn->edid_blob_ptr->data;
+ hdmi->dvi_mode = !drm_detect_monitor_audio(edid);
+ } else {
+ dev_err(hdmi->dev, "No edid in drm_connector object\n");
+ }

mtk_hdmi_output_set_display_mode(hdmi, &hdmi->mode);
clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]);
@@ -1417,7 +1410,6 @@ static const struct drm_bridge_funcs mtk_hdmi_bridge_funcs = {
.atomic_pre_enable = mtk_hdmi_bridge_atomic_pre_enable,
.atomic_enable = mtk_hdmi_bridge_atomic_enable,
.detect = mtk_hdmi_bridge_detect,
- .get_edid = mtk_hdmi_bridge_get_edid,
};

static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi,
@@ -1425,7 +1417,7 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi,
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
- struct device_node *cec_np, *remote, *i2c_np;
+ struct device_node *cec_np, *remote;
struct platform_device *cec_pdev;
struct regmap *regmap;
struct resource *mem;
@@ -1497,24 +1489,6 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi,
}
}

- i2c_np = of_parse_phandle(remote, "ddc-i2c-bus", 0);
- if (!i2c_np) {
- dev_err(dev, "Failed to find ddc-i2c-bus node in %pOF\n",
- remote);
- of_node_put(remote);
- ret = -EINVAL;
- goto put_device;
- }
- of_node_put(remote);
-
- hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np);
- of_node_put(i2c_np);
- if (!hdmi->ddc_adpt) {
- dev_err(dev, "Failed to get ddc i2c adapter by node\n");
- ret = -EINVAL;
- goto put_device;
- }
-
return 0;
put_device:
put_device(hdmi->cec_dev);
@@ -1728,8 +1702,7 @@ static int mtk_drm_hdmi_probe(struct platform_device *pdev)

hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs;
hdmi->bridge.of_node = pdev->dev.of_node;
- hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
- | DRM_BRIDGE_OP_HPD;
+ hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_HPD;
hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
drm_bridge_add(&hdmi->bridge);

--
2.39.2.637.g21b0678d19-goog


2023-02-18 11:18:10

by Pin-yen Lin

[permalink] [raw]
Subject: [PATCH v3 4/5] dt-bindings: display: bridge: Add GPIO display mux binding

From: Nicolas Boichat <[email protected]>

Add bindings for Generic GPIO mux driver.

Signed-off-by: Nicolas Boichat <[email protected]>
Signed-off-by: Pin-yen Lin <[email protected]>
---
Rob mentioned that this series can probably use the common MUX bindings.
Those bindings are designed for MUX consumers that requests a certain
MUX configuration, while this driver is reading a GPIO pin controlled
by the other parts of the system (in our use case, it's the embedded
controller firmware).

Changes in v3:
- Removed ddc-i2c-bus in the bindings

Changes in v2:
- Referenced existing dt-binding schemas from graph.yaml
- Added ddc-i2c-bus into the bindings

.../bindings/display/bridge/gpio-mux.yaml | 90 +++++++++++++++++++
1 file changed, 90 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/bridge/gpio-mux.yaml

diff --git a/Documentation/devicetree/bindings/display/bridge/gpio-mux.yaml b/Documentation/devicetree/bindings/display/bridge/gpio-mux.yaml
new file mode 100644
index 000000000000..e6b82ea2f45c
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/gpio-mux.yaml
@@ -0,0 +1,90 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/gpio-mux.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic display mux (1 input, 2 outputs)
+
+maintainers:
+ - Nicolas Boichat <[email protected]>
+
+description: |
+ This bindings describes a simple display (e.g. HDMI) mux, that has 1
+ input, and 2 outputs. The mux status is controlled by hardware, and
+ its status is read back using a GPIO.
+
+properties:
+ compatible:
+ const: gpio-display-mux
+
+ detect-gpios:
+ maxItems: 1
+ description: GPIO that indicates the active output
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: |
+ Video port for input.
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: |
+ 2 video ports for output.
+ The reg value in the endpoints matches the GPIO status: when
+ GPIO is asserted, endpoint with reg value <1> is selected.
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - compatible
+ - detect-gpios
+ - ports
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ hdmi_mux: hdmi_mux {
+ compatible = "gpio-display-mux";
+ detect-gpios = <&pio 36 GPIO_ACTIVE_HIGH>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmi_mux_pins>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 { /* input */
+ reg = <0>;
+
+ hdmi_mux_in: endpoint {
+ remote-endpoint = <&hdmi0_out>;
+ };
+ };
+
+ port@1 { /* output */
+ reg = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hdmi_mux_out_anx: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&dp_bridge_in>;
+ };
+
+ hdmi_mux_out_hdmi: endpoint@1 {
+ reg = <1>;
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+ };
+ };
+ };
--
2.39.2.637.g21b0678d19-goog


2023-02-18 11:18:18

by Pin-yen Lin

[permalink] [raw]
Subject: [PATCH v3 5/5] drm: bridge: Generic GPIO mux driver

From: Nicolas Boichat <[email protected]>

This driver supports single input, 2 output display mux (e.g.
HDMI mux), that provide its status via a GPIO.

Signed-off-by: Nicolas Boichat <[email protected]>
Signed-off-by: Pin-yen Lin <[email protected]>

---

Changes in v3:
- Added .get_edid callback in generic-gpio-mux driver

Changes in v2:
- Dropped attach/mode_set/enable/disable callbacks
- Fixed style issues
- Removed the special case for the HDMI connector
- Made the driver only read the GPIO status in IRQ handler
- Rebased to drm-misc-next
- Update the license: "GPL v2" --> "GPL"

drivers/gpu/drm/bridge/Kconfig | 10 +
drivers/gpu/drm/bridge/Makefile | 1 +
drivers/gpu/drm/bridge/generic-gpio-mux.c | 222 ++++++++++++++++++++++
3 files changed, 233 insertions(+)
create mode 100644 drivers/gpu/drm/bridge/generic-gpio-mux.c

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 12e8f30c65f7..42d7c418e8ff 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -73,6 +73,16 @@ config DRM_FSL_LDB
help
Support for i.MX8MP DPI-to-LVDS on-SoC encoder.

+config DRM_GENERIC_GPIO_MUX
+ tristate "Generic GPIO-controlled mux"
+ depends on OF
+ select DRM_KMS_HELPER
+ help
+ This bridge driver models a GPIO-controlled display mux with one
+ input, 2 outputs (e.g. an HDMI mux). The hardware decides which output
+ is active, reports it as a GPIO, and the driver redirects calls to the
+ appropriate downstream bridge (if any).
+
config DRM_ITE_IT6505
tristate "ITE IT6505 DisplayPort bridge"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 52f6e8b4a821..4d6d7f63330c 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
obj-$(CONFIG_DRM_CROS_EC_ANX7688) += cros-ec-anx7688.o
obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
obj-$(CONFIG_DRM_FSL_LDB) += fsl-ldb.o
+obj-$(CONFIG_DRM_GENERIC_GPIO_MUX) += generic-gpio-mux.o
obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
obj-$(CONFIG_DRM_LONTIUM_LT8912B) += lontium-lt8912b.o
obj-$(CONFIG_DRM_LONTIUM_LT9211) += lontium-lt9211.o
diff --git a/drivers/gpu/drm/bridge/generic-gpio-mux.c b/drivers/gpu/drm/bridge/generic-gpio-mux.c
new file mode 100644
index 000000000000..928edde701fa
--- /dev/null
+++ b/drivers/gpu/drm/bridge/generic-gpio-mux.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Generic gpio mux bridge driver
+ *
+ * Copyright 2016 Google LLC
+ */
+
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_probe_helper.h>
+
+struct gpio_display_mux {
+ struct device *dev;
+
+ struct gpio_desc *gpiod_detect;
+ int detect_irq;
+ int cur_next;
+
+ struct drm_bridge bridge;
+
+ struct drm_bridge *next[2];
+};
+
+static inline struct gpio_display_mux *bridge_to_gpio_display_mux(
+ struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct gpio_display_mux, bridge);
+}
+
+static irqreturn_t gpio_display_mux_det_threaded_handler(int unused, void *data)
+{
+ struct gpio_display_mux *mux = data;
+ int active = gpiod_get_value(mux->gpiod_detect);
+
+ if (active < 0) {
+ dev_err(mux->dev, "Failed to get detect GPIO\n");
+ return IRQ_HANDLED;
+ }
+
+ dev_dbg(mux->dev, "Interrupt %d!\n", active);
+ mux->cur_next = active;
+
+ if (mux->bridge.dev)
+ drm_kms_helper_hotplug_event(mux->bridge.dev);
+
+ return IRQ_HANDLED;
+}
+
+static bool gpio_display_mux_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct gpio_display_mux *mux = bridge_to_gpio_display_mux(bridge);
+ struct drm_bridge *next;
+
+ next = mux->next[mux->cur_next];
+
+ /* Assume that we have a most one bridge in both downstreams */
+ if (next && next->funcs->mode_fixup)
+ return next->funcs->mode_fixup(next, mode, adjusted_mode);
+
+ return true;
+}
+
+static struct edid *gpio_display_mux_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct gpio_display_mux *mux = bridge_to_gpio_display_mux(bridge);
+ struct drm_bridge *next;
+
+ next = mux->next[mux->cur_next];
+
+ return next->funcs->get_edid(next, connector);
+}
+
+static const struct drm_bridge_funcs gpio_display_mux_bridge_funcs = {
+ .mode_fixup = gpio_display_mux_mode_fixup,
+ .get_edid = gpio_display_mux_get_edid,
+};
+
+static int gpio_display_mux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_display_mux *mux;
+ struct device_node *port, *ep, *remote;
+ int ret;
+ u32 reg;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mux);
+ mux->dev = &pdev->dev;
+
+ mux->bridge.of_node = dev->of_node;
+
+ mux->gpiod_detect = devm_gpiod_get(dev, "detect", GPIOD_IN);
+ if (IS_ERR(mux->gpiod_detect))
+ return PTR_ERR(mux->gpiod_detect);
+
+ mux->detect_irq = gpiod_to_irq(mux->gpiod_detect);
+ if (mux->detect_irq < 0) {
+ dev_err(dev, "Failed to get output irq %d\n",
+ mux->detect_irq);
+ return -ENODEV;
+ }
+
+ port = of_graph_get_port_by_id(dev->of_node, 1);
+ if (!port) {
+ dev_err(dev, "Missing output port node\n");
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(port, ep) {
+ if (!ep->name || (of_node_cmp(ep->name, "endpoint") != 0)) {
+ of_node_put(ep);
+ continue;
+ }
+
+ if (of_property_read_u32(ep, "reg", &reg) < 0 ||
+ reg >= ARRAY_SIZE(mux->next)) {
+ dev_err(dev,
+ "Missing/invalid reg property for endpoint %s\n",
+ ep->full_name);
+ of_node_put(ep);
+ of_node_put(port);
+ return -EINVAL;
+ }
+
+ remote = of_graph_get_remote_port_parent(ep);
+ if (!remote) {
+ dev_err(dev,
+ "Missing connector/bridge node for endpoint %s\n",
+ ep->full_name);
+ of_node_put(ep);
+ of_node_put(port);
+ return -EINVAL;
+ }
+
+ mux->next[reg] = of_drm_find_bridge(remote);
+ if (!mux->next[reg]) {
+ dev_err(dev, "Waiting for external bridge %s\n",
+ remote->name);
+ of_node_put(ep);
+ of_node_put(remote);
+ of_node_put(port);
+ return -EPROBE_DEFER;
+ }
+
+ of_node_put(remote);
+ }
+ of_node_put(port);
+
+ /*
+ * Because the next bridges are not registered to the drm bridge chain,
+ * we have to set DRM_BRIDGE_OP_EDID here and trigger the .get_edid
+ * callbacks of the actual connectors.
+ */
+ if (mux->next[0] && mux->next[0]->ops & DRM_BRIDGE_OP_EDID &&
+ mux->next[1] && mux->next[1]->ops & DRM_BRIDGE_OP_EDID)
+ mux->bridge.ops |= DRM_BRIDGE_OP_EDID;
+
+ mux->bridge.funcs = &gpio_display_mux_bridge_funcs;
+ mux->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
+ drm_bridge_add(&mux->bridge);
+
+ ret = devm_request_threaded_irq(dev, mux->detect_irq, NULL,
+ gpio_display_mux_det_threaded_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "gpio-display-mux-det", mux);
+ if (ret) {
+ dev_err(dev, "Failed to request MUX_DET threaded irq\n");
+ goto err_bridge_remove;
+ }
+
+ return 0;
+
+err_bridge_remove:
+ drm_bridge_remove(&mux->bridge);
+
+ return ret;
+}
+
+static int gpio_display_mux_remove(struct platform_device *pdev)
+{
+ struct gpio_display_mux *mux = platform_get_drvdata(pdev);
+
+ disable_irq(mux->detect_irq);
+ drm_bridge_remove(&mux->bridge);
+
+ return 0;
+}
+
+static const struct of_device_id gpio_display_mux_match[] = {
+ { .compatible = "gpio-display-mux", },
+ {},
+};
+
+struct platform_driver gpio_display_mux_driver = {
+ .probe = gpio_display_mux_probe,
+ .remove = gpio_display_mux_remove,
+ .driver = {
+ .name = "gpio-display-mux",
+ .of_match_table = gpio_display_mux_match,
+ },
+};
+
+module_platform_driver(gpio_display_mux_driver);
+
+MODULE_DESCRIPTION("GPIO-controlled display mux");
+MODULE_AUTHOR("Nicolas Boichat <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.39.2.637.g21b0678d19-goog


2023-02-21 15:42:37

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v3 1/5] dt-bindings: display: bridge: Add ddc-i2c-bus for anx7688

On Sat, Feb 18, 2023 at 07:17:08PM +0800, Pin-yen Lin wrote:
> Introduce a optional "ddc-i2c-bus" property for anx7688 bridge. This
> allows the bridge to register a .get_edid callback.

What's .get_edid? This is a binding and is independent of Linux.

>
> Signed-off-by: Pin-yen Lin <[email protected]>
> ---
>
> Changes in v3:
> - New in v3
>
> .../bindings/display/bridge/google,cros-ec-anx7688.yaml | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml b/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml
> index a44d025d33bd..9d5ce8172e88 100644
> --- a/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml
> +++ b/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml
> @@ -25,6 +25,10 @@ properties:
> maxItems: 1
> description: I2C address of the device.
>
> + ddc-i2c-bus:
> + description: phandle link to the I2C controller used for DDC EDID probing
> + $ref: /schemas/types.yaml#/definitions/phandle
> +

No, this belongs in the connector node. The DDC signals are routed to
the connector, not the bridge chip.

Rob

2023-02-23 13:37:26

by Pin-yen Lin

[permalink] [raw]
Subject: Re: [PATCH v3 1/5] dt-bindings: display: bridge: Add ddc-i2c-bus for anx7688

(Resend this with plaintext mode. Sorry for the previous spam.)

Hi Rob,

After some internal discussions, we decided to drop this series
because the driver is trying to do runtime routing of the display
signals, which is not supported by the current DRM system, even if we
can describe the design in the device tree. I'm explaining our use
case as a record, and I can follow up on the mtk_hdmi.c change if
anyone is interested.

The more detailed schematics looks like:
+----------+ +----------------+ +-----------+
| MTK | | | | HDMI |
| HDMI +--->| TS3DV642 +--->| connector |
| bridge | | | +-----------+
+----------+ | |
| HDMI MUX | +-------------+
| | | anx7688 | +-----------+
+----------+ | | | HDMI to DP | | USB-C |
| cros EC +--->| +--->| bridge +-->| connector |
+----------+ +----------------+ +-------------+ +-----------+

The TS3DV642 HDMI MUX is controlled by the CrOS EC to switch the HDMI
signal between two output ports, and the generic-display-mux driver
was trying to control the downstream bridges according to the GPIO
status reported by EC. However, as far as I know, this kind of runtime
routing is not supported by DRM.

And, it can work fine if we only describe the following design in DT:

+---------+ +-------------+ +-----------+
| MTK | | anx7688 | | USB-C |
| HDMI +-->| HDMI to DP +-->| connector |
| bridge | | bridge | +-----------+
+---------+ +-------------+

This can work because the anx7688 driver won't reject any display
modes when the DP lane count is 0 ([1]), which is true when the HDMI
connector is used.

Thanks for your time for reviewing this series and sorry for not
explaining everything clearly at the beginning.

[1]: https://elixir.bootlin.com/linux/v6.2/source/drivers/gpu/drm/bridge/cros-ec-anx7688.c#L87

Regards,
Pin-yen


On Tue, Feb 21, 2023 at 11:41 PM Rob Herring <[email protected]> wrote:
>
> On Sat, Feb 18, 2023 at 07:17:08PM +0800, Pin-yen Lin wrote:
> > Introduce a optional "ddc-i2c-bus" property for anx7688 bridge. This
> > allows the bridge to register a .get_edid callback.
>
> What's .get_edid? This is a binding and is independent of Linux.
>
> >
> > Signed-off-by: Pin-yen Lin <[email protected]>
> > ---
> >
> > Changes in v3:
> > - New in v3
> >
> > .../bindings/display/bridge/google,cros-ec-anx7688.yaml | 5 +++++
> > 1 file changed, 5 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml b/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml
> > index a44d025d33bd..9d5ce8172e88 100644
> > --- a/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml
> > +++ b/Documentation/devicetree/bindings/display/bridge/google,cros-ec-anx7688.yaml
> > @@ -25,6 +25,10 @@ properties:
> > maxItems: 1
> > description: I2C address of the device.
> >
> > + ddc-i2c-bus:
> > + description: phandle link to the I2C controller used for DDC EDID probing
> > + $ref: /schemas/types.yaml#/definitions/phandle
> > +
>
> No, this belongs in the connector node. The DDC signals are routed to
> the connector, not the bridge chip.
>
> Rob

2023-03-29 18:12:46

by Jagan Teki

[permalink] [raw]
Subject: Re: [PATCH v3 0/5] Add generic-display-mux driver and bindings

On Sat, Feb 18, 2023 at 4:47 PM Pin-yen Lin <[email protected]> wrote:
>
> This series is developed for and tested on MT8173 board, and the layout is:
>
> /-- anx7688
> -- MT8173 HDMI bridge -- GPIO mux
> \-- native HDMI

What is the part number of this GPIO mux? Does mux gpio interrupt
based and able to switch output HDMI and DP. If so, how this gpio
interrupt is different than the HPD in native HDMI? I mean does HPD
have any detection bottlenecks with mux gpio interrupt?

I do have GPIO Mux that input DSI and two outputs DMD/HDMI Input and
HDMI Out. 2x1 with 1x2 mux.

I'm thinking having a generic mux to select the specific out number of
outputs with associated interface outputs can be a viable solution to
address all use cases here.

Thanks,
Jagan.

2023-03-30 04:19:51

by Chen-Yu Tsai

[permalink] [raw]
Subject: Re: [PATCH v3 0/5] Add generic-display-mux driver and bindings

On Thu, Mar 30, 2023 at 2:10 AM Jagan Teki <[email protected]> wrote:
>
> On Sat, Feb 18, 2023 at 4:47 PM Pin-yen Lin <[email protected]> wrote:
> >
> > This series is developed for and tested on MT8173 board, and the layout is:
> >
> > /-- anx7688
> > -- MT8173 HDMI bridge -- GPIO mux
> > \-- native HDMI
>
> What is the part number of this GPIO mux? Does mux gpio interrupt
> based and able to switch output HDMI and DP. If so, how this gpio
> interrupt is different than the HPD in native HDMI? I mean does HPD
> have any detection bottlenecks with mux gpio interrupt?

It's a full blown HDMI 1:2 demultiplexer, so DDC and HPD signals are muxed
as well. The anx7688 handles all the Type-C stuff, and our EC talks to it.
If HDMI output from the SoC needs to be muxed over for DP alt-mode, the EC
will signal the SoC.

> I do have GPIO Mux that input DSI and two outputs DMD/HDMI Input and
> HDMI Out. 2x1 with 1x2 mux.
>
> I'm thinking having a generic mux to select the specific out number of
> outputs with associated interface outputs can be a viable solution to
> address all use cases here.

AFAIK DRM doesn't allow dynamically changing the constitution of a display
pipeline, so it doesn't really work out for us.

If you're just muxing between different outputs with passive/dumb bridges,
it may still work for you.

ChenYu