lkml.org 
[lkml]   [2017]   [Jun]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v3] drm/bridge: Refactor out the panel wrapper from the lvds-encoder bridge.
    Date
    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.

    v2: Fix build with CONFIG_DRM=m, drop "dev" argument that should just
    be the panel's dev, move kerneldoc up a level and document
    _remove().
    v3: Fix another breakage with CONFIG_DRM=m, fix breakage with
    CONFIG_OF=n, move protos under CONFIG_DRM_PANEL_BRIDGE, wrap a
    line.

    Signed-off-by: Eric Anholt <eric@anholt.net>
    Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> (v1)
    Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com> (v2)
    Acked-by: Archit Taneja <architt@codeaurora.org> (v2)
    ---

    New version of the first patch with build fixes. I've re-pushed to
    get another round of kbuild test, but if it comes back clean, I'd like
    to merge this one, the vc4 patches (unchanged), and atmel-hlcdc (acked
    by the maintainer). I'd be dropping my STM patch (replaced by their
    DSI series), and mediatek (I'd like an ack from the maintainer first).

    Documentation/gpu/drm-kms-helpers.rst | 6 +
    drivers/gpu/drm/Makefile | 1 +
    drivers/gpu/drm/bridge/Kconfig | 11 +-
    drivers/gpu/drm/bridge/lvds-encoder.c | 157 +++-----------------------
    drivers/gpu/drm/bridge/panel.c | 200 ++++++++++++++++++++++++++++++++++
    include/drm/drm_bridge.h | 7 ++
    6 files changed, 241 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..7c5e2549a58a 100644
    --- a/Documentation/gpu/drm-kms-helpers.rst
    +++ b/Documentation/gpu/drm-kms-helpers.rst
    @@ -143,6 +143,12 @@ Bridge Helper Reference
    .. kernel-doc:: drivers/gpu/drm/drm_bridge.c
    :export:

    +Panel-Bridge Helper Reference
    +-----------------------------
    +
    +.. kernel-doc:: drivers/gpu/drm/bridge/panel.c
    + :export:
    +
    .. _drm_panel_helper:

    Panel Helper Reference
    diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
    index acc88942c2e5..dc69175255b1 100644
    --- a/drivers/gpu/drm/Makefile
    +++ b/drivers/gpu/drm/Makefile
    @@ -24,6 +24,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o
    drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
    drm-$(CONFIG_PCI) += ati_pcigart.o
    drm-$(CONFIG_DRM_PANEL) += drm_panel.o
    +drm-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
    drm-$(CONFIG_OF) += drm_of.o
    drm-$(CONFIG_AGP) += drm_agpsupport.o
    drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
    diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
    index f6968d3b4b41..adf9ae0e0b7c 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
    + depends on 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/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c
    index f1f67a279426..0903ba574f61 100644
    --- a/drivers/gpu/drm/bridge/lvds-encoder.c
    +++ b/drivers/gpu/drm/bridge/lvds-encoder.c
    @@ -8,144 +8,18 @@
    */

    #include <drm/drmP.h>
    -#include <drm/drm_atomic_helper.h>
    -#include <drm/drm_connector.h>
    -#include <drm/drm_crtc_helper.h>
    -#include <drm/drm_encoder.h>
    -#include <drm/drm_modeset_helper_vtables.h>
    +#include <drm/drm_bridge.h>
    #include <drm/drm_panel.h>

    #include <linux/of_graph.h>

    -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,34 @@ 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(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..99f9a4beb859
    --- /dev/null
    +++ b/drivers/gpu/drm/bridge/panel.c
    @@ -0,0 +1,200 @@
    +/*
    + * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
    + * 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 <drm/drmP.h>
    +#include <drm/drm_panel.h>
    +#include <drm/drm_atomic_helper.h>
    +#include <drm/drm_connector.h>
    +#include <drm/drm_crtc_helper.h>
    +#include <drm/drm_encoder.h>
    +#include <drm/drm_modeset_helper_vtables.h>
    +#include <drm/drm_panel.h>
    +
    +struct panel_bridge {
    + struct drm_bridge bridge;
    + 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 calls the appropriate functions from drm_panel.
    + *
    + * @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. When done with the bridge,
    + * drm_bridge_detach() should be called as normal, then
    + * drm_panel_bridge_remove() to free it.
    + */
    +struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
    + u32 connector_type)
    +{
    + struct panel_bridge *panel_bridge;
    + int ret;
    +
    + if (!panel)
    + return ERR_PTR(EINVAL);
    +
    + panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
    + GFP_KERNEL);
    + if (!panel_bridge)
    + return ERR_PTR(-ENOMEM);
    +
    + panel_bridge->connector_type = connector_type;
    + panel_bridge->panel = panel;
    +
    + panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
    +#ifdef CONFIG_OF
    + panel_bridge->bridge.of_node = panel->dev->of_node;
    +#endif
    +
    + ret = drm_bridge_add(&panel_bridge->bridge);
    + if (ret)
    + return ERR_PTR(ret);
    +
    + return &panel_bridge->bridge;
    +}
    +EXPORT_SYMBOL(drm_panel_bridge_add);
    +
    +/**
    + * drm_panel_bridge_remove - Unregisters and frees a drm_bridge
    + * created by drm_panel_bridge_add().
    + *
    + * @bridge: The drm_bridge being freed.
    + */
    +void drm_panel_bridge_remove(struct drm_bridge *bridge)
    +{
    + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
    +
    + drm_bridge_remove(bridge);
    + devm_kfree(panel_bridge->panel->dev, bridge);
    +}
    +EXPORT_SYMBOL(drm_panel_bridge_remove);
    diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
    index 5b106eca6d57..1dc94d5392e2 100644
    --- a/include/drm/drm_bridge.h
    +++ b/include/drm/drm_bridge.h
    @@ -29,6 +29,7 @@
    #include <drm/drm_modes.h>

    struct drm_bridge;
    +struct drm_panel;

    /**
    * struct drm_bridge_funcs - drm_bridge control functions
    @@ -263,4 +264,10 @@ 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_BRIDGE
    +struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
    + u32 connector_type);
    +void drm_panel_bridge_remove(struct drm_bridge *bridge);
    +#endif
    +
    #endif
    --
    2.11.0
    \
     
     \ /
      Last update: 2017-06-05 00:15    [W:7.174 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site