From 0d60131a3b5dcb4c6980eb30cbca3c6fb62d7b6f Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Wed, 13 Nov 2019 15:51:24 +0000 Subject: drm/bridge: Repurpose lvds-encoder.c lvds-encoder.c implementation is also suitable for LVDS decoders, not just LVDS encoders. Instead of creating a new driver for addressing support for transparent LVDS decoders, repurpose lvds-encoder.c for the greater good with this patch. This patch only "rebrands" the lvds-encoder.c driver, to make it suitable for hosting LVDS decoders support. The actual support for LVDS decoders will come with a later patch. Signed-off-by: Fabrizio Castro Reviewed-by: Laurent Pinchart Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/1573660292-10629-6-git-send-email-fabrizio.castro@bp.renesas.com --- drivers/gpu/drm/bridge/lvds-codec.c | 154 ++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 drivers/gpu/drm/bridge/lvds-codec.c (limited to 'drivers/gpu/drm/bridge/lvds-codec.c') diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c new file mode 100644 index 000000000000..b5801a287a0a --- /dev/null +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2019 Renesas Electronics Corporation + * Copyright (C) 2016 Laurent Pinchart + */ + +#include +#include +#include +#include +#include + +#include +#include + +struct lvds_codec { + struct drm_bridge bridge; + struct drm_bridge *panel_bridge; + struct gpio_desc *powerdown_gpio; +}; + +static int lvds_codec_attach(struct drm_bridge *bridge) +{ + struct lvds_codec *lvds_codec = container_of(bridge, + struct lvds_codec, bridge); + + return drm_bridge_attach(bridge->encoder, lvds_codec->panel_bridge, + bridge); +} + +static void lvds_codec_enable(struct drm_bridge *bridge) +{ + struct lvds_codec *lvds_codec = container_of(bridge, + struct lvds_codec, bridge); + + if (lvds_codec->powerdown_gpio) + gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 0); +} + +static void lvds_codec_disable(struct drm_bridge *bridge) +{ + struct lvds_codec *lvds_codec = container_of(bridge, + struct lvds_codec, bridge); + + if (lvds_codec->powerdown_gpio) + gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 1); +} + +static struct drm_bridge_funcs funcs = { + .attach = lvds_codec_attach, + .enable = lvds_codec_enable, + .disable = lvds_codec_disable, +}; + +static int lvds_codec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *port; + struct device_node *endpoint; + struct device_node *panel_node; + struct drm_panel *panel; + struct lvds_codec *lvds_codec; + + lvds_codec = devm_kzalloc(dev, sizeof(*lvds_codec), GFP_KERNEL); + if (!lvds_codec) + return -ENOMEM; + + lvds_codec->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(lvds_codec->powerdown_gpio)) { + int err = PTR_ERR(lvds_codec->powerdown_gpio); + + if (err != -EPROBE_DEFER) + dev_err(dev, "powerdown GPIO failure: %d\n", err); + return err; + } + + /* Locate the panel DT node. */ + port = of_graph_get_port_by_id(dev->of_node, 1); + if (!port) { + dev_dbg(dev, "port 1 not found\n"); + return -ENXIO; + } + + endpoint = of_get_child_by_name(port, "endpoint"); + of_node_put(port); + if (!endpoint) { + dev_dbg(dev, "no endpoint for port 1\n"); + return -ENXIO; + } + + panel_node = of_graph_get_remote_port_parent(endpoint); + of_node_put(endpoint); + if (!panel_node) { + dev_dbg(dev, "no remote endpoint for port 1\n"); + return -ENXIO; + } + + panel = of_drm_find_panel(panel_node); + of_node_put(panel_node); + if (IS_ERR(panel)) { + dev_dbg(dev, "panel not found, deferring probe\n"); + return PTR_ERR(panel); + } + + lvds_codec->panel_bridge = + devm_drm_panel_bridge_add_typed(dev, panel, + DRM_MODE_CONNECTOR_LVDS); + if (IS_ERR(lvds_codec->panel_bridge)) + return PTR_ERR(lvds_codec->panel_bridge); + + /* + * The panel_bridge bridge is attached to the panel's of_node, + * but we need a bridge attached to our of_node for our user + * to look up. + */ + lvds_codec->bridge.of_node = dev->of_node; + lvds_codec->bridge.funcs = &funcs; + drm_bridge_add(&lvds_codec->bridge); + + platform_set_drvdata(pdev, lvds_codec); + + return 0; +} + +static int lvds_codec_remove(struct platform_device *pdev) +{ + struct lvds_codec *lvds_codec = platform_get_drvdata(pdev); + + drm_bridge_remove(&lvds_codec->bridge); + + return 0; +} + +static const struct of_device_id lvds_codec_match[] = { + { .compatible = "lvds-encoder" }, + { .compatible = "thine,thc63lvdm83d" }, + {}, +}; +MODULE_DEVICE_TABLE(of, lvds_codec_match); + +static struct platform_driver lvds_codec_driver = { + .probe = lvds_codec_probe, + .remove = lvds_codec_remove, + .driver = { + .name = "lvds-codec", + .of_match_table = lvds_codec_match, + }, +}; +module_platform_driver(lvds_codec_driver); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("LVDS encoders and decoders"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e6f607bb2ba8aac719cf6abdf1d9dd8dae0c63e5 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Wed, 18 Dec 2019 01:07:53 +0200 Subject: drm/bridge: lvds-codec: Add "lvds-decoder" support Add support for transparent LVDS decoders by adding a new compatible string ("lvds-decoder") to the driver. This patch also adds member connector_type to struct lvds_codec, and that's because LVDS decoders have a different connector type from LVDS encoders. We fill this new member up with the data matching the compatible string. Signed-off-by: Fabrizio Castro Reviewed-by: Laurent Pinchart [Fix pointer to int cast warning] Signed-off-by: Laurent Pinchart Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20191217230753.2999-1-laurent.pinchart+renesas@ideasonboard.com --- drivers/gpu/drm/bridge/lvds-codec.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/bridge/lvds-codec.c') diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index b5801a287a0a..d072a76b20d9 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,7 @@ struct lvds_codec { struct drm_bridge bridge; struct drm_bridge *panel_bridge; struct gpio_desc *powerdown_gpio; + u32 connector_type; }; static int lvds_codec_attach(struct drm_bridge *bridge) @@ -65,6 +67,7 @@ static int lvds_codec_probe(struct platform_device *pdev) if (!lvds_codec) return -ENOMEM; + lvds_codec->connector_type = (uintptr_t)of_device_get_match_data(dev); lvds_codec->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(lvds_codec->powerdown_gpio)) { @@ -105,7 +108,7 @@ static int lvds_codec_probe(struct platform_device *pdev) lvds_codec->panel_bridge = devm_drm_panel_bridge_add_typed(dev, panel, - DRM_MODE_CONNECTOR_LVDS); + lvds_codec->connector_type); if (IS_ERR(lvds_codec->panel_bridge)) return PTR_ERR(lvds_codec->panel_bridge); @@ -133,8 +136,18 @@ static int lvds_codec_remove(struct platform_device *pdev) } static const struct of_device_id lvds_codec_match[] = { - { .compatible = "lvds-encoder" }, - { .compatible = "thine,thc63lvdm83d" }, + { + .compatible = "lvds-decoder", + .data = (void *)DRM_MODE_CONNECTOR_DPI, + }, + { + .compatible = "lvds-encoder", + .data = (void *)DRM_MODE_CONNECTOR_LVDS, + }, + { + .compatible = "thine,thc63lvdm83d", + .data = (void *)DRM_MODE_CONNECTOR_LVDS, + }, {}, }; MODULE_DEVICE_TABLE(of, lvds_codec_match); -- cgit v1.2.3 From 319d8e9814c4da2c6ea0a8e080b3ffa517542daf Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Wed, 13 Nov 2019 15:51:26 +0000 Subject: drm/bridge: lvds-codec: Simplify panel DT node localisation The probe function needs to get ahold of the panel device tree node, and it achieves that by using a combination of of_graph_get_port_by_id, of_get_child_by_name, and of_graph_get_remote_port_parent. We can achieve the same goal by replacing those calls with a call to of_graph_get_remote_node these days. Signed-off-by: Fabrizio Castro Reviewed-by: Laurent Pinchart Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/1573660292-10629-8-git-send-email-fabrizio.castro@bp.renesas.com --- drivers/gpu/drm/bridge/lvds-codec.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'drivers/gpu/drm/bridge/lvds-codec.c') diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index d072a76b20d9..5f04cc11227e 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -57,8 +57,6 @@ static struct drm_bridge_funcs funcs = { static int lvds_codec_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *port; - struct device_node *endpoint; struct device_node *panel_node; struct drm_panel *panel; struct lvds_codec *lvds_codec; @@ -79,23 +77,9 @@ static int lvds_codec_probe(struct platform_device *pdev) } /* Locate the panel DT node. */ - port = of_graph_get_port_by_id(dev->of_node, 1); - if (!port) { - dev_dbg(dev, "port 1 not found\n"); - return -ENXIO; - } - - endpoint = of_get_child_by_name(port, "endpoint"); - of_node_put(port); - if (!endpoint) { - dev_dbg(dev, "no endpoint for port 1\n"); - return -ENXIO; - } - - panel_node = of_graph_get_remote_port_parent(endpoint); - of_node_put(endpoint); + panel_node = of_graph_get_remote_node(dev->of_node, 1, 0); if (!panel_node) { - dev_dbg(dev, "no remote endpoint for port 1\n"); + dev_dbg(dev, "panel DT node not found\n"); return -ENXIO; } -- cgit v1.2.3