From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752835AbdECJbH (ORCPT ); Wed, 3 May 2017 05:31:07 -0400 Received: from galahad.ideasonboard.com ([185.26.127.97]:48173 "EHLO galahad.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751661AbdECJa6 (ORCPT ); Wed, 3 May 2017 05:30:58 -0400 From: Laurent Pinchart To: Archit Taneja Cc: Eric Anholt , dri-devel@lists.freedesktop.org, Yannick Fertre , linux-kernel@vger.kernel.org, Thierry Reding , Andrzej Hajda Subject: Re: [PATCH 1/2] drm/bridge: Refactor out the panel wrapper from the lvds-encoder bridge. Date: Wed, 03 May 2017 12:32:11 +0300 Message-ID: <21970543.rOK40RzAVr@avalon> User-Agent: KMail/4.14.10 (Linux/4.9.16-gentoo; KDE/4.14.29; x86_64; ; ) In-Reply-To: References: <20170427163601.7313-1-eric@anholt.net> MIME-Version: 1.0 Content-Transfer-Encoding: 7Bit Content-Type: text/plain; charset="us-ascii" Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Archit, On Wednesday 03 May 2017 14:53:00 Archit Taneja wrote: > On 04/27/2017 10:06 PM, Eric Anholt wrote: > > Many DRM drivers have common code to make a stub connector > > implementation that wraps a drm_panel. By wrapping the panel in a DRM > > bridge, all of the connector code (including calls during encoder > > enable/disable) goes away. > > > > Signed-off-by: Eric Anholt > > --- > > > > Documentation/gpu/drm-kms-helpers.rst | 3 + > > drivers/gpu/drm/bridge/Kconfig | 11 +- > > drivers/gpu/drm/bridge/Makefile | 1 + > > drivers/gpu/drm/bridge/lvds-encoder.c | 158 ++++------------------------ > > drivers/gpu/drm/bridge/panel.c | 188 +++++++++++++++++++++++++++++ > > include/drm/drm_bridge.h | 8 ++ > > 6 files changed, 228 insertions(+), 141 deletions(-) > > create mode 100644 drivers/gpu/drm/bridge/panel.c > > > > diff --git a/Documentation/gpu/drm-kms-helpers.rst > > b/Documentation/gpu/drm-kms-helpers.rst index c075aadd7078..60eb3b41702b > > 100644 > > --- a/Documentation/gpu/drm-kms-helpers.rst > > +++ b/Documentation/gpu/drm-kms-helpers.rst > > @@ -143,6 +143,9 @@ Bridge Helper Reference > > .. kernel-doc:: drivers/gpu/drm/drm_bridge.c > > :export: > > > > +.. kernel-doc:: drivers/gpu/drm/bridge/panel.c > > + :export: > > + > > .. _drm_panel_helper: > > Panel Helper Reference > > > > diff --git a/drivers/gpu/drm/bridge/Kconfig > > b/drivers/gpu/drm/bridge/Kconfig index f6968d3b4b41..c4daca38743c 100644 > > --- a/drivers/gpu/drm/bridge/Kconfig > > +++ b/drivers/gpu/drm/bridge/Kconfig > > @@ -4,6 +4,14 @@ config DRM_BRIDGE > > help > > Bridge registration and lookup framework. > > > > +config DRM_PANEL_BRIDGE > > + def_bool y > > + depends on DRM_BRIDGE > > + select DRM_KMS_HELPER > > + select DRM_PANEL > > + help > > + DRM bridge wrapper of DRM panels > > + > > menu "Display Interface Bridges" > > depends on DRM && DRM_BRIDGE > > > > @@ -27,8 +35,7 @@ config DRM_DUMB_VGA_DAC > > config DRM_LVDS_ENCODER > > tristate "Transparent parallel to LVDS encoder support" > > depends on OF > > - select DRM_KMS_HELPER > > - select DRM_PANEL > > + select DRM_PANEL_BRIDGE > > help > > Support for transparent parallel to LVDS encoders that don't require > > any configuration. > > diff --git a/drivers/gpu/drm/bridge/Makefile > > b/drivers/gpu/drm/bridge/Makefile index 3fe2226ee2f2..40a43750ad55 100644 > > --- a/drivers/gpu/drm/bridge/Makefile > > +++ b/drivers/gpu/drm/bridge/Makefile > > @@ -5,6 +5,7 @@ obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o > > obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o > > obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += > > megachips-stdpxxxx-ge-b850v3-fw.o obj-$(CONFIG_DRM_NXP_PTN3460) += > > nxp-ptn3460.o > > +obj-$(CONFIG_DRM_PANEL_BRIDGE) += panel.o > > obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o > > obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o > > obj-$(CONFIG_DRM_SII902X) += sii902x.o > > diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c > > b/drivers/gpu/drm/bridge/lvds-encoder.c index f1f67a279426..04e1504c4d8f > > 100644 > > --- a/drivers/gpu/drm/bridge/lvds-encoder.c > > +++ b/drivers/gpu/drm/bridge/lvds-encoder.c > > @@ -8,144 +8,18 @@ > > */ > > > > #include > > -#include > > -#include > > -#include > > -#include > > -#include > > +#include > > #include > > > > #include > > > > -struct lvds_encoder { > > - struct device *dev; > > - > > - struct drm_bridge bridge; > > - struct drm_connector connector; > > - struct drm_panel *panel; > > -}; > > - > > -static inline struct lvds_encoder * > > -drm_bridge_to_lvds_encoder(struct drm_bridge *bridge) > > -{ > > - return container_of(bridge, struct lvds_encoder, bridge); > > -} > > - > > -static inline struct lvds_encoder * > > -drm_connector_to_lvds_encoder(struct drm_connector *connector) > > -{ > > - return container_of(connector, struct lvds_encoder, connector); > > -} > > - > > -static int lvds_connector_get_modes(struct drm_connector *connector) > > -{ > > - struct lvds_encoder *lvds = drm_connector_to_lvds_encoder(connector); > > - > > - return drm_panel_get_modes(lvds->panel); > > -} > > - > > -static const struct drm_connector_helper_funcs > > lvds_connector_helper_funcs = { - .get_modes = lvds_connector_get_modes, > > -}; > > - > > -static const struct drm_connector_funcs lvds_connector_funcs = { > > - .dpms = drm_atomic_helper_connector_dpms, > > - .reset = drm_atomic_helper_connector_reset, > > - .fill_modes = drm_helper_probe_single_connector_modes, > > - .destroy = drm_connector_cleanup, > > - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > > - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > > -}; > > - > > -static int lvds_encoder_attach(struct drm_bridge *bridge) > > -{ > > - struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); > > - struct drm_connector *connector = &lvds->connector; > > - int ret; > > - > > - if (!bridge->encoder) { > > - DRM_ERROR("Missing encoder\n"); > > - return -ENODEV; > > - } > > - > > - drm_connector_helper_add(connector, &lvds_connector_helper_funcs); > > - > > - ret = drm_connector_init(bridge->dev, connector, &lvds_connector_funcs, > > - DRM_MODE_CONNECTOR_LVDS); > > - if (ret) { > > - DRM_ERROR("Failed to initialize connector\n"); > > - return ret; > > - } > > - > > - drm_mode_connector_attach_encoder(&lvds->connector, bridge->encoder); > > - > > - ret = drm_panel_attach(lvds->panel, &lvds->connector); > > - if (ret < 0) > > - return ret; > > - > > - return 0; > > -} > > - > > -static void lvds_encoder_detach(struct drm_bridge *bridge) > > -{ > > - struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); > > - > > - drm_panel_detach(lvds->panel); > > -} > > - > > -static void lvds_encoder_pre_enable(struct drm_bridge *bridge) > > -{ > > - struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); > > - > > - drm_panel_prepare(lvds->panel); > > -} > > - > > -static void lvds_encoder_enable(struct drm_bridge *bridge) > > -{ > > - struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); > > - > > - drm_panel_enable(lvds->panel); > > -} > > - > > -static void lvds_encoder_disable(struct drm_bridge *bridge) > > -{ > > - struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); > > - > > - drm_panel_disable(lvds->panel); > > -} > > - > > -static void lvds_encoder_post_disable(struct drm_bridge *bridge) > > -{ > > - struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge); > > - > > - drm_panel_unprepare(lvds->panel); > > -} > > - > > -static const struct drm_bridge_funcs lvds_encoder_bridge_funcs = { > > - .attach = lvds_encoder_attach, > > - .detach = lvds_encoder_detach, > > - .pre_enable = lvds_encoder_pre_enable, > > - .enable = lvds_encoder_enable, > > - .disable = lvds_encoder_disable, > > - .post_disable = lvds_encoder_post_disable, > > -}; > > - > > > > static int lvds_encoder_probe(struct platform_device *pdev) > > { > > > > - struct lvds_encoder *lvds; > > > > struct device_node *port; > > struct device_node *endpoint; > > > > - struct device_node *panel; > > - > > - lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); > > - if (!lvds) > > - return -ENOMEM; > > - > > - lvds->dev = &pdev->dev; > > - platform_set_drvdata(pdev, lvds); > > - > > - lvds->bridge.funcs = &lvds_encoder_bridge_funcs; > > - lvds->bridge.of_node = pdev->dev.of_node; > > + struct device_node *panel_node; > > + struct drm_panel *panel; > > + struct drm_bridge *bridge; > > > > /* Locate the panel DT node. */ > > port = of_graph_get_port_by_id(pdev->dev.of_node, 1); > > > > @@ -161,29 +35,35 @@ static int lvds_encoder_probe(struct platform_device > > *pdev)> > > return -ENXIO; > > > > } > > > > - panel = of_graph_get_remote_port_parent(endpoint); > > + panel_node = of_graph_get_remote_port_parent(endpoint); > > > > of_node_put(endpoint); > > > > - if (!panel) { > > + if (!panel_node) { > > > > dev_dbg(&pdev->dev, "no remote endpoint for port 1\n"); > > return -ENXIO; > > > > } > > > > - lvds->panel = of_drm_find_panel(panel); > > - of_node_put(panel); > > - if (!lvds->panel) { > > + panel = of_drm_find_panel(panel_node); > > + of_node_put(panel_node); > > + if (!panel) { > > > > dev_dbg(&pdev->dev, "panel not found, deferring probe\n"); > > return -EPROBE_DEFER; > > > > } > > > > - /* Register the bridge. */ > > - return drm_bridge_add(&lvds->bridge); > > + bridge = drm_panel_bridge_add(&pdev->dev, panel, > > + DRM_MODE_CONNECTOR_LVDS); > > + if (IS_ERR(bridge)) > > + return PTR_ERR(bridge); > > + > > + platform_set_drvdata(pdev, bridge); > > + > > + return 0; > > > > } > > > > static int lvds_encoder_remove(struct platform_device *pdev) > > { > > > > - struct lvds_encoder *encoder = platform_get_drvdata(pdev); > > + struct drm_bridge *bridge = platform_get_drvdata(pdev); > > > > - drm_bridge_remove(&encoder->bridge); > > + drm_bridge_remove(bridge); > > > > return 0; > > > > } > > > > diff --git a/drivers/gpu/drm/bridge/panel.c > > b/drivers/gpu/drm/bridge/panel.c new file mode 100644 > > index 000000000000..2081245455c6 > > --- /dev/null > > +++ b/drivers/gpu/drm/bridge/panel.c > > @@ -0,0 +1,188 @@ > > +/* > > + * Copyright (C) 2016 Laurent Pinchart > > > > + * Copyright (C) 2017 Broadcom > > + * > > + * This program is free software; you can redistribute it and/or > > + * modify it under the terms of the GNU General Public License as > > + * published by the Free Software Foundation; either version 2 of > > + * the License, or (at your option) any later version. > > + */ > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +struct panel_bridge { > > + struct drm_bridge bridge; > > + struct device *dev; > > + struct drm_connector connector; > > + struct drm_panel *panel; > > + u32 connector_type; > > +}; > > + > > +static inline struct panel_bridge * > > +drm_bridge_to_panel_bridge(struct drm_bridge *bridge) > > +{ > > + return container_of(bridge, struct panel_bridge, bridge); > > +} > > + > > +static inline struct panel_bridge * > > +drm_connector_to_panel_bridge(struct drm_connector *connector) > > +{ > > + return container_of(connector, struct panel_bridge, connector); > > +} > > + > > +static int panel_bridge_connector_get_modes(struct drm_connector > > *connector) +{ > > + struct panel_bridge *panel_bridge = > > + drm_connector_to_panel_bridge(connector); > > + > > + return drm_panel_get_modes(panel_bridge->panel); > > +} > > + > > +static const struct drm_connector_helper_funcs > > panel_bridge_connector_helper_funcs = { + .get_modes = > > panel_bridge_connector_get_modes, > > +}; > > + > > +static const struct drm_connector_funcs panel_bridge_connector_funcs = { > > + .dpms = drm_atomic_helper_connector_dpms, > > + .reset = drm_atomic_helper_connector_reset, > > + .fill_modes = drm_helper_probe_single_connector_modes, > > + .destroy = drm_connector_cleanup, > > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > > +}; > > + > > +static int panel_bridge_attach(struct drm_bridge *bridge) > > +{ > > + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); > > + struct drm_connector *connector = &panel_bridge->connector; > > + int ret; > > + > > + if (!bridge->encoder) { > > + DRM_ERROR("Missing encoder\n"); > > + return -ENODEV; > > + } > > + > > + drm_connector_helper_add(connector, > > + &panel_bridge_connector_helper_funcs); > > + > > + ret = drm_connector_init(bridge->dev, connector, > > + &panel_bridge_connector_funcs, > > + panel_bridge->connector_type); > > + if (ret) { > > + DRM_ERROR("Failed to initialize connector\n"); > > + return ret; > > + } > > + > > + drm_mode_connector_attach_encoder(&panel_bridge->connector, > > + bridge->encoder); > > + > > + ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector); > > + if (ret < 0) > > + return ret; > > + > > + return 0; > > +} > > + > > +static void panel_bridge_detach(struct drm_bridge *bridge) > > +{ > > + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); > > + > > + drm_panel_detach(panel_bridge->panel); > > +} > > + > > +static void panel_bridge_pre_enable(struct drm_bridge *bridge) > > +{ > > + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); > > + > > + drm_panel_prepare(panel_bridge->panel); > > +} > > + > > +static void panel_bridge_enable(struct drm_bridge *bridge) > > +{ > > + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); > > + > > + drm_panel_enable(panel_bridge->panel); > > +} > > + > > +static void panel_bridge_disable(struct drm_bridge *bridge) > > +{ > > + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); > > + > > + drm_panel_disable(panel_bridge->panel); > > +} > > + > > +static void panel_bridge_post_disable(struct drm_bridge *bridge) > > +{ > > + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); > > + > > + drm_panel_unprepare(panel_bridge->panel); > > +} > > + > > +static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { > > + .attach = panel_bridge_attach, > > + .detach = panel_bridge_detach, > > + .pre_enable = panel_bridge_pre_enable, > > + .enable = panel_bridge_enable, > > + .disable = panel_bridge_disable, > > + .post_disable = panel_bridge_post_disable, > > +}; > > + > > +/** > > + * drm_panel_bridge_add - Creates a drm_bridge and drm_connector that > > + * just call the appropriate functions from drm_panel. > > + * > > + * @dev: The struct device of the panel device. This is used for > > + * registering the drm_bridge. > > + * @panel: The drm_panel being wrapped. Must be non-NULL. > > + * @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be > > + * created. > > + * > > + * For drivers converting from directly using drm_panel: The expected > > + * usage pattern is that during either encoder module probe or DSI > > + * host attach, a drm_panel will be looked up through > > + * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to > > + * wrap that panel in the new bridge, and the result can then be > > + * passed to drm_bridge_attach(). The drm_panel_prepare() and related > > + * functions can be dropped from the encoder driver (they're now > > + * called by the KMS helpers before calling into the encoder), along > > + * with connector creation. > > +panel/bridge reviewers. Thank you for that. > This does make things much cleaner, but it seems a bit strange to create > a drm_bridge when there isn't really a HW bridge in the display chain (i.e, > when the DSI encoder is directly connected to a DSI panel). I agree with you. I don't think this is a good idea, and what I'd like to see is a refactoring of the panel and bridge ops that would make them more alike, allowing to handle bridges and panels through a common interface. Another reason to go in that direction is that, if we create an object that can expose both encoder-related and connector-related operations (bridges being geared towards the former and panel the latter at the moment), then we'll be able to handle connector creation in a much easier better way. As I've explained before, bridges should not create connectors, as they have no idea whether they're chained to another bridge or output directly to a connector. > There are kms drivers that use drm_panel, but don't have simple stub > connectors that wrap around a drm_panel. They have more complicated > connector ops, and may call drm_panel_prepare() and related functions a bit > differently. We won't be able to use drm_panel_bridge for those drivers. > > For msm, we check whether the DSI encoder is connected directly to a panel > or an external bridge. If it's connected to an external bridge, we skip the > creation of the stub connector, and rely on the external bridge driver to > create the connector: > > http://lxr.free-electrons.com/source/drivers/gpu/drm/msm/dsi/dsi.c#L227 > > The msm solution isn't very neat, but it avoids the need to create another > bridge to glue things together. > > Archit > > > + */ > > +struct drm_bridge *drm_panel_bridge_add(struct device *dev, > > + struct drm_panel *panel, > > + u32 connector_type) > > +{ > > + struct panel_bridge *panel_bridge = > > + devm_kzalloc(dev, sizeof(*panel_bridge), GFP_KERNEL); > > + int ret;tegra_output_connector_detect, > > + > > + if (!dev || !panel) > > + return ERR_PTR(EINVAL); > > + > > + panel_bridge->dev = dev; > > + panel_bridge->connector_type = connector_type; > > + panel_bridge->panel = panel; > > + > > + panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; > > + panel_bridge->bridge.of_node = dev->of_node; > > + > > + ret = drm_bridge_add(&panel_bridge->bridge); > > + if (ret) > > + return ERR_PTR(ret); > > + > > + return &panel_bridge->bridge; > > +} > > +EXPORT_SYMBOL(drm_panel_bridge_add); > > + > > +void drm_panel_bridge_remove(struct device *dev, struct drm_bridge > > *bridge) +{ > > + drm_bridge_remove(bridge); > > + devm_kfree(dev, bridge); > > +} > > +EXPORT_SYMBOL(drm_panel_bridge_remove); > > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h > > index fdd82fcbf168..e08563ee0546 100644 > > --- a/include/drm/drm_bridge.h > > +++ b/include/drm/drm_bridge.h > > @@ -29,6 +29,7 @@ > > #include > > > > struct drm_bridge; > > +struct drm_panel; > > > > /** > > * struct drm_bridge_funcs - drm_bridge control functions > > @@ -221,4 +222,11 @@ void drm_bridge_mode_set(struct drm_bridge *bridge, > > void drm_bridge_pre_enable(struct drm_bridge *bridge); > > void drm_bridge_enable(struct drm_bridge *bridge); > > > > +#ifdef CONFIG_DRM_PANEL > > +struct drm_bridge *drm_panel_bridge_add(struct device *dev, > > + struct drm_panel *panel, > > + u32 connector_type); > > +void drm_panel_bridge_remove(struct device *dev, struct drm_bridge > > *bridge); > > +#endif > > + > > #endif -- Regards, Laurent Pinchart